diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 747c6cf1d..1a045bdb3 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -38,6 +38,10 @@ source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/tegra-vde/Kconfig" +source "drivers/staging/media/vpu-vc8000d-kernel/Kconfig" + +source "drivers/staging/media/vpu-vc8000e-kernel/Kconfig" + source "drivers/staging/media/zoran/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index b59571826..4d874f88f 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ +obj-$(CONFIG_VIDEO_VC8000D) += vpu-vc8000d-kernel/linux/subsys_driver/ vpu-vc8000d-kernel/linux/memalloc/ +obj-$(CONFIG_VIDEO_VC8000E) += vpu-vc8000e-kernel/linux/kernel_module/ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/COPYING b/drivers/staging/media/vpu-vc8000d-kernel/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/drivers/staging/media/vpu-vc8000d-kernel/Kconfig b/drivers/staging/media/vpu-vc8000d-kernel/Kconfig new file mode 100644 index 000000000..51e6f2e84 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/Kconfig @@ -0,0 +1,3 @@ +config VIDEO_VC8000D + tristate "VC8000D support" + default m diff --git a/drivers/staging/media/vpu-vc8000d-kernel/Makefile b/drivers/staging/media/vpu-vc8000d-kernel/Makefile new file mode 100644 index 000000000..3c580db9c --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/Makefile @@ -0,0 +1,80 @@ +## + # Copyright (C) 2020 Alibaba Group Holding Limited +## + +DIR_TARGET_KO =bsp/vdec/ko + +MODULE_NAME=VDEC +BUILD_LOG_START="\033[47;30m>>> $(MODULE_NAME) $@ begin\033[0m" +BUILD_LOG_END ="\033[47;30m<<< $(MODULE_NAME) $@ end\033[0m" + +# +# Do a parallel build with multiple jobs, based on the number of CPUs online +# in this system: 'make -j8' on a 8-CPU system, etc. +# +# (To override it, run 'make JOBS=1' and similar.) +# +ifeq ($(JOBS),) + JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) + ifeq ($(JOBS),) + JOBS := 1 + endif +endif + +all: driver install_local_output +.PHONY: driver install_local_output install_addons install_prepare clean_driver clean_output clean + +info: + @echo $(BUILD_LOG_START) + @echo " ====== Build Info from repo project ======" + @echo " BUILDROOT_DIR="$(BUILDROOT_DIR) + @echo " CROSS_COMPILE="$(CROSS_COMPILE) + @echo " LINUX_DIR="$(LINUX_DIR) + @echo " ARCH="$(ARCH) + @echo " BOARD_NAME="$(BOARD_NAME) + @echo " KERNEL_ID="$(KERNELVERSION) + @echo " KERNEL_DIR="$(LINUX_DIR) + @echo " INSTALL_DIR_SDK="$(INSTALL_DIR_SDK) + @echo " ====== Build configuration by settings ======" + @echo " JOBS="$(JOBS) + @echo $(BUILD_LOG_END) + +driver: + @echo $(BUILD_LOG_START) + make -C linux/subsys_driver KDIR=$(LINUX_DIR) CROSS=$(CROSS_COMPILE) ARCH=$(ARCH) + make -C linux/memalloc KDIR=$(LINUX_DIR) CROSS=$(CROSS_COMPILE) ARCH=$(ARCH) + @echo $(BUILD_LOG_END) + +clean_driver: + @echo $(BUILD_LOG_START) + make -C linux/subsys_driver clean + make -C linux/memalloc clean + @echo $(BUILD_LOG_END) + +install_prepare: + mkdir -p ./output/rootfs/$(DIR_TARGET_KO) + +install_addons: install_prepare + @if [ -d addons/ko ]; then \ + cp -rf addons/ko/* ./output/rootfs/$(DIR_TARGET_KO); \ + fi + +install_local_output: driver install_prepare install_addons + @echo $(BUILD_LOG_START) + find ./linux -name "*.ko" | xargs -i cp -f {} ./output/rootfs/$(DIR_TARGET_KO) + cp -f ./linux/subsys_driver/driver_load.sh ./output/rootfs/$(DIR_TARGET_KO) + cp -f ./linux/memalloc/memalloc_load.sh ./output/rootfs/$(DIR_TARGET_KO) + chmod +x ./output/rootfs/$(DIR_TARGET_KO)/*.sh + echo "hantrodec" > ./output/rootfs/$(DIR_TARGET_KO)/vc8000d.conf + @if [ `command -v tree` != "" ]; then \ + tree ./output/rootfs; \ + fi + @echo $(BUILD_LOG_END) + +clean_output: + @echo $(BUILD_LOG_START) + rm -rf ./output + @echo $(BUILD_LOG_END) + +clean: clean_output clean_driver + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/README.md b/drivers/staging/media/vpu-vc8000d-kernel/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/insmod.sh b/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/insmod.sh new file mode 100755 index 000000000..77c9364f6 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/insmod.sh @@ -0,0 +1,5 @@ +#!/bin/sh +KERNEL_VER=$(uname -r) +BASE_PATH=/lib/modules/${KERNEL_VER}/extra + +insmod $BASE_PATH/hantrodec.ko diff --git a/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/rmmod.sh b/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/rmmod.sh new file mode 100755 index 000000000..60e80e53a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/addons/ko/rmmod.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rmmod hantrodec diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/dwl/dwl_defs.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/dwl/dwl_defs.h new file mode 100644 index 000000000..e7e5c635c --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/dwl/dwl_defs.h @@ -0,0 +1,142 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef SOFTWARE_LINUX_DWL_DWL_DEFS_H_ +#define SOFTWARE_LINUX_DWL_DWL_DEFS_H_ + +#define DWL_MPEG2_E 31 /* 1 bit */ +#define DWL_VC1_E 29 /* 2 bits */ +#define DWL_JPEG_E 28 /* 1 bit */ +#define DWL_HJPEG_E 17 /* 1 bit */ +#define DWL_AV1_E 16 /* 1 bit */ +#define DWL_MPEG4_E 26 /* 2 bits */ +#define DWL_H264_E 24 /* 2 bits */ +#define DWL_H264HIGH10_E 20 /* 1 bits */ +#define DWL_AVS2_E 18 /* 2 bits */ +#define DWL_VP6_E 23 /* 1 bit */ +#define DWL_RV_E 26 /* 2 bits */ +#define DWL_VP8_E 23 /* 1 bit */ +#define DWL_VP7_E 24 /* 1 bit */ +#define DWL_WEBP_E 19 /* 1 bit */ +#define DWL_AVS_E 22 /* 1 bit */ +#define DWL_G1_PP_E 16 /* 1 bit */ +#define DWL_G2_PP_E 31 /* 1 bit */ +#define DWL_PP_E 31 /* 1 bit */ +#define DWL_HEVC_E 26 /* 3 bits */ +#define DWL_VP9_E 29 /* 3 bits */ + +#define DWL_H264_PIPELINE_E 31 /* 1 bit */ +#define DWL_JPEG_PIPELINE_E 30 /* 1 bit */ + +#define DWL_G2_HEVC_E 0 /* 1 bits */ +#define DWL_G2_VP9_E 1 /* 1 bits */ +#define DWL_G2_RFC_E 2 /* 1 bits */ +#define DWL_RFC_E 17 /* 2 bits */ +#define DWL_G2_DS_E 3 /* 1 bits */ +#define DWL_DS_E 28 /* 3 bits */ +#define DWL_HEVC_VER 8 /* 4 bits */ +#define DWL_VP9_PROFILE 12 /* 3 bits */ +#define DWL_RING_E 16 /* 1 bits */ + +#define HANTRODEC_IRQ_STAT_DEC 1 +#define HANTRODEC_IRQ_STAT_DEC_OFF (HANTRODEC_IRQ_STAT_DEC * 4) +#define BIGOCEAN_IRQ_STAT_DEC 2 +#define BIGOCEAN_IRQ_STAT_DEC_OFF (HANTRODEC_IRQ_STAT_DEC * 4) + +#define HANTRODECPP_SYNTH_CFG 60 +#define HANTRODECPP_SYNTH_CFG_OFF (HANTRODECPP_SYNTH_CFG * 4) +#define HANTRODEC_SYNTH_CFG 50 +#define HANTRODEC_SYNTH_CFG_OFF (HANTRODEC_SYNTH_CFG * 4) +#define HANTRODEC_SYNTH_CFG_2 54 +#define HANTRODEC_SYNTH_CFG_2_OFF (HANTRODEC_SYNTH_CFG_2 * 4) +#define HANTRODEC_SYNTH_CFG_3 56 +#define HANTRODEC_SYNTH_CFG_3_OFF (HANTRODEC_SYNTH_CFG_3 * 4) +#define HANTRODEC_CFG_STAT 23 +#define HANTRODEC_CFG_STAT_OFF (HANTRODEC_CFG_STAT * 4) +#define HANTRODECPP_CFG_STAT 260 +#define HANTRODECPP_CFG_STAT_OFF (HANTRODECPP_CFG_STAT * 4) + + +#define HANTRODEC_DEC_E 0x01 +#define HANTRODEC_PP_E 0x01 +#define HANTRODEC_DEC_ABORT 0x20 +#define HANTRODEC_DEC_IRQ_DISABLE 0x10 +#define HANTRODEC_DEC_IRQ 0x100 + +/* Legacy from G1 */ +#define HANTRO_IRQ_STAT_DEC 1 +#define HANTRO_IRQ_STAT_DEC_OFF (HANTRO_IRQ_STAT_DEC * 4) +#define HANTRO_IRQ_STAT_PP 60 +#define HANTRO_IRQ_STAT_PP_OFF (HANTRO_IRQ_STAT_PP * 4) + +#define HANTROPP_SYNTH_CFG 100 +#define HANTROPP_SYNTH_CFG_OFF (HANTROPP_SYNTH_CFG * 4) +#define HANTRODEC_SYNTH_CFG 50 +#define HANTRODEC_SYNTH_CFG_OFF (HANTRODEC_SYNTH_CFG * 4) +#define HANTRODEC_SYNTH_CFG_2 54 +#define HANTRODEC_SYNTH_CFG_2_OFF (HANTRODEC_SYNTH_CFG_2 * 4) + +/* VC8000D HW build id */ +#define HANTRODEC_HW_BUILD_ID 309 +#define HANTRODEC_HW_BUILD_ID_OFF (HANTRODEC_HW_BUILD_ID * 4) + +#define HANTRO_DEC_E 0x01 +#define HANTRO_PP_E 0x01 +#define HANTRO_DEC_ABORT 0x20 +#define HANTRO_DEC_IRQ_DISABLE 0x10 +#define HANTRO_PP_IRQ_DISABLE 0x10 +#define HANTRO_DEC_IRQ 0x100 +#define HANTRO_PP_IRQ 0x100 + +#endif /* SOFTWARE_LINUX_DWL_DWL_DEFS_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/Makefile b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/Makefile new file mode 100644 index 000000000..4b9c83788 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/Makefile @@ -0,0 +1,88 @@ +############################################################################# +# +# The MIT License (MIT) +# +# Copyright (c) 2014 - 2021 VERISILICON +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +############################################################################# +# +# The GPL License (GPL) +# +# Copyright (C) 2014 - 2021 VERISILICON +# +# 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. +# +############################################################################# +# +# Note: This software is released under dual MIT and GPL licenses. A +# recipient may use this file under the terms of either the MIT license or +# GPL License. If you wish to use only one license not the other, you can +# indicate your decision by deleting one of the above license notices in your +# version of this file. +# +############################################################################## + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DMEMALLOC_DEBUG +else + DEBFLAGS = -O2 +endif + +# base address and size (MB) for linear memory allocation +HLINA_START := 0x02000000 +HLINA_SIZE := 96 + +EXTRA_CFLAGS += $(DEBFLAGS) +EXTRA_CFLAGS += -DHLINA_START_ADDRESS=$(HLINA_START) -DHLINA_SIZE=$(HLINA_SIZE) + +ifneq ($(KERNELRELEASE),) +# recursive call from kernel build system + +memalloc-objs := +obj-m += memalloc.o + +else + +#KDIR := /export/Testing/Board_Version_Control/SW_Common/SOCLE_MDK-3D/openlinux/2.6.29/v0_5/android_linux-2.6.29 +KDIR := /lib/modules/3.2.0-36-generic/build + +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +endif + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + rm -rf modules.order Module.symvers diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/README b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/README new file mode 100644 index 000000000..ab0b89cbb --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/README @@ -0,0 +1,44 @@ +============================================================================== + USAGE +============================================================================== + +./memalloc_load.sh + +=============================================================================== + What is memalloc? +=============================================================================== + +memalloc is a test environment specific memory allocation kernel driver. +It uses memory left outside linux memory management, divides it into chunks and +gives base addresses to processes for these chunks(= a block of memory). + +It has been written for test memory purposes for a very specific test +environment and nothing more. + +=============================================================================== + Loading the memalloc kernel driver +=============================================================================== + +1. compile the kernel driver in software/linux/memalloc +> make + +2. For testing load kernel driver with: +>./memalloc_load.sh + +...or if you want to specify the max size in MB of linear memory: +>./memalloc_load.sh alloc_size=400 + +...or if you want to specify the base address of linear memory: +>./memalloc_load.sh alloc_base=0x42000000 + +...or both: +>./memalloc_load.sh alloc_base=0x42000000 alloc_size=400 + +3. Debugging: +the device node should show up in the the directory /dev +The device should show up in the list /proc/devices +> ls /dev +> cat /proc/devices +Kernel messages can be viewed with +> dmesg +More kernel debug prints can be enabled in the Makefile diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/build_for_pcie.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/build_for_pcie.sh new file mode 100755 index 000000000..b28f5a6d0 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/build_for_pcie.sh @@ -0,0 +1,22 @@ +#!/bin/sh + + +KVER=$(uname -r) +KDIR=/lib/modules/$KVER/build + +HLINA_START=0x30000000 +HLINA_SIZE=512 #Size in megabytes + +HLINA_END=$(($HLINA_START + $HLINA_SIZE*1024*1024)) + +echo +echo "KDIR=$KDIR" +echo "CROSS=$CROSS" +echo +echo "Linear memory base = $HLINA_START" +printf "Linear top-of-memory = 0x%x\n" $HLINA_END +echo "Linear memory size = ${HLINA_SIZE}MB" +echo + +make KDIR=$KDIR CROSS=$CROSS HLINA_START=${HLINA_START}U HLINA_SIZE=${HLINA_SIZE}U $1 +exit $? diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.c new file mode 100644 index 000000000..d9af14688 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.c @@ -0,0 +1,369 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "memalloc.h" + +#ifndef HLINA_START_ADDRESS +#define HLINA_START_ADDRESS 0x1e0000000 +#endif + +#ifndef HLINA_SIZE +#define HLINA_SIZE 512 +#endif + +#ifndef HLINA_TRANSL_OFFSET +#define HLINA_TRANSL_OFFSET 0x0 +#endif + +/* the size of chunk in MEMALLOC_DYNAMIC */ +#define CHUNK_SIZE (PAGE_SIZE * 4) + +/* memory size in MBs for MEMALLOC_DYNAMIC */ +unsigned long alloc_size = HLINA_SIZE; +unsigned long alloc_base = HLINA_START_ADDRESS; + +/* user space SW will substract HLINA_TRANSL_OFFSET from the bus address + * and decoder HW will use the result as the address translated base + * address. The SW needs the original host memory bus address for memory + * mapping to virtual address. */ +unsigned long addr_transl = HLINA_TRANSL_OFFSET; + +static int memalloc_major = 0; /* dynamic */ + +/* module_param(name, type, perm) */ +module_param(alloc_size, ulong, 0); +module_param(alloc_base, ulong, 0); +module_param(addr_transl, ulong, 0); + +static DEFINE_SPINLOCK(mem_lock); + +typedef struct hlinc { + u64 bus_address; + u32 chunks_reserved; + const struct file *filp; /* Client that allocated this chunk */ +} hlina_chunk; + +static hlina_chunk *hlina_chunks = NULL; +static size_t chunks = 0; + +static int AllocMemory(unsigned long *busaddr, unsigned int size, + const struct file *filp); +static int FreeMemory(unsigned long busaddr, const struct file *filp); +static void ResetMems(void); + +static long memalloc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { + int ret = 0; + MemallocParams memparams; + unsigned long busaddr; + + PDEBUG("ioctl cmd 0x%08x\n", cmd); + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != MEMALLOC_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > MEMALLOC_IOC_MAXNR) return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_READ) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) + ret = !access_ok(arg, _IOC_SIZE(cmd)); +#else + ret = !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd)); +#endif + else if (_IOC_DIR(cmd) & _IOC_WRITE) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) + ret = !access_ok(arg, _IOC_SIZE(cmd)); +#else + ret = !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd)); +#endif + if (ret) return -EFAULT; + + switch (cmd) { + case MEMALLOC_IOCHARDRESET: + PDEBUG("HARDRESET\n"); + ResetMems(); + break; + case MEMALLOC_IOCXGETBUFFER: + PDEBUG("GETBUFFER"); + + ret = copy_from_user(&memparams, (MemallocParams *)arg, + sizeof(MemallocParams)); + if (ret) break; + + ret = AllocMemory(&memparams.bus_address, memparams.size, filp); + + memparams.translation_offset = addr_transl; + + ret |= copy_to_user((MemallocParams *)arg, &memparams, + sizeof(MemallocParams)); + + break; + case MEMALLOC_IOCSFREEBUFFER: + PDEBUG("FREEBUFFER\n"); + + __get_user(busaddr, (unsigned long *)arg); + ret = FreeMemory(busaddr, filp); + break; + } + + return ret ? -EFAULT : 0; +} + +static int memalloc_open(struct inode *inode, struct file *filp) { + + PDEBUG("dev opened\n"); + return 0; +} + +static int memalloc_release(struct inode *inode, struct file *filp) { + int i = 0; + + spin_lock(&mem_lock); + + for (i = 0; i < chunks; i++) { + if (hlina_chunks[i].filp == filp) { + printk(KERN_WARNING "memalloc: Found unfreed memory at release time!\n"); + + hlina_chunks[i].filp = NULL; + hlina_chunks[i].chunks_reserved = 0; + } + } + + spin_unlock(&mem_lock); + PDEBUG("dev closed\n"); + return 0; +} + +void __exit memalloc_cleanup(void) { + if (hlina_chunks != NULL) vfree(hlina_chunks); + + unregister_chrdev(memalloc_major, "memalloc"); + + PDEBUG("module removed\n"); + return; +} + +/* VFS methods */ +static struct file_operations memalloc_fops = { + .owner = THIS_MODULE, + .open = memalloc_open, + .release = memalloc_release, + .unlocked_ioctl = memalloc_ioctl +}; + +int __init memalloc_init(void) { + int result; + + PDEBUG("module init\n"); + printk("memalloc: Linear Memory Allocator\n"); + printk("memalloc: Linear memory base = 0x%llx\n", alloc_base); + + chunks = (alloc_size * 1024 * 1024) / CHUNK_SIZE; + + printk(KERN_INFO + "memalloc: Total size %ld MB; %d chunks" + " of size %lu\n", + alloc_size, (int)chunks, CHUNK_SIZE); + + hlina_chunks = (hlina_chunk *)vmalloc(chunks * sizeof(hlina_chunk)); + if (hlina_chunks == NULL) { + printk(KERN_ERR "memalloc: cannot allocate hlina_chunks\n"); + result = -ENOMEM; + goto err; + } + + result = register_chrdev(memalloc_major, "memalloc", &memalloc_fops); + if (result < 0) { + PDEBUG("memalloc: unable to get major %d\n", memalloc_major); + goto err; + } else if (result != 0) {/* this is for dynamic major */ + memalloc_major = result; + } + + ResetMems(); + + return 0; + +err: + if (hlina_chunks != NULL) vfree(hlina_chunks); + + return result; +} + +/* Cycle through the buffers we have, give the first free one */ +static int AllocMemory(unsigned long *busaddr, unsigned int size, + const struct file *filp) { + + int i = 0; + int j = 0; + unsigned int skip_chunks = 0; + + /* calculate how many chunks we need; round up to chunk boundary */ + unsigned int alloc_chunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE; + + *busaddr = 0; + + spin_lock(&mem_lock); + + /* run through the chunk table */ + for (i = 0; i < chunks;) { + skip_chunks = 0; + /* if this chunk is available */ + if (!hlina_chunks[i].chunks_reserved) { + /* check that there is enough memory left */ + if (i + alloc_chunks > chunks) break; + + /* check that there is enough consecutive chunks available */ + for (j = i; j < i + alloc_chunks; j++) { + if (hlina_chunks[j].chunks_reserved) { + skip_chunks = 1; + /* skip the used chunks */ + i = j + hlina_chunks[j].chunks_reserved; + break; + } + } + + /* if enough free memory found */ + if (!skip_chunks) { + *busaddr = hlina_chunks[i].bus_address; + hlina_chunks[i].filp = filp; + hlina_chunks[i].chunks_reserved = alloc_chunks; + break; + } + } else { + /* skip the used chunks */ + i += hlina_chunks[i].chunks_reserved; + } + } + + spin_unlock(&mem_lock); + + if (*busaddr == 0) { + printk("memalloc: Allocation FAILED: size = %ld\n", size); + return -EFAULT; + } else { + PDEBUG("MEMALLOC OK: size: %d, reserved: %ld\n", size, + alloc_chunks * CHUNK_SIZE); + } + + return 0; +} + +/* Free a buffer based on bus address */ +static int FreeMemory(unsigned long busaddr, const struct file *filp) { + int i = 0; + + spin_lock(&mem_lock); + + for (i = 0; i < chunks; i++) { + /* user space SW has stored the translated bus address, add addr_transl to + * translate back to our address space */ + if (hlina_chunks[i].bus_address == busaddr + addr_transl) { + if (hlina_chunks[i].filp == filp) { + hlina_chunks[i].filp = NULL; + hlina_chunks[i].chunks_reserved = 0; + } else { + printk(KERN_WARNING "memalloc: Owner mismatch while freeing memory!\n"); + } + break; + } + } + + spin_unlock(&mem_lock); + + return 0; +} + +/* Reset "used" status */ +static void ResetMems(void) { + int i = 0; + unsigned long ba = alloc_base; + + spin_lock(&mem_lock); + + for (i = 0; i < chunks; i++) { + hlina_chunks[i].bus_address = ba; + hlina_chunks[i].filp = NULL; + hlina_chunks[i].chunks_reserved = 0; + + ba += CHUNK_SIZE; + } + + spin_unlock(&mem_lock); +} + +module_init(memalloc_init); +module_exit(memalloc_cleanup); + +/* module description */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Verisilicon"); +MODULE_DESCRIPTION("Linear RAM allocation"); diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.h new file mode 100644 index 000000000..bb438ebe3 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc.h @@ -0,0 +1,88 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef MEMALLOC_H +#define MEMALLOC_H + +#include + +#undef PDEBUG +#ifdef MEMALLOC_DEBUG +#ifdef __KERNEL__ +#define PDEBUG(fmt, args...) printk(KERN_INFO "memalloc: " fmt, ##args) +#else +#define PDEBUG(fmt, args...) fprintf(stderr, fmt, ##args) +#endif +#else +#define PDEBUG(fmt, args...) +#endif + +typedef struct { + unsigned long bus_address; + unsigned int size; + unsigned long translation_offset; + unsigned int mem_type; + int fd; + int flags; +} MemallocParams; + +#define MEMALLOC_IOC_MAGIC 'k' + +#define MEMALLOC_IOCXGETBUFFER _IOWR(MEMALLOC_IOC_MAGIC, 1, MemallocParams*) +#define MEMALLOC_IOCSFREEBUFFER _IOW(MEMALLOC_IOC_MAGIC, 2, unsigned long*) + +#define MEMALLOC_IOCHARDRESET _IO(MEMALLOC_IOC_MAGIC, 15) /* debugging tool */ +#define MEMALLOC_IOC_MAXNR 15 + +#endif /* MEMALLOC_H */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc_load.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc_load.sh new file mode 100755 index 000000000..e0e9fe614 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/memalloc_load.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +############################################################################# +# +# The MIT License (MIT) +# +# Copyright (c) 2014 - 2021 VERISILICON +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +############################################################################# +# +# The GPL License (GPL) +# +# Copyright (C) 2014 - 2021 VERISILICON +# +# 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. +# +############################################################################# +# +# Note: This software is released under dual MIT and GPL licenses. A +# recipient may use this file under the terms of either the MIT license or +# GPL License. If you wish to use only one license not the other, you can +# indicate your decision by deleting one of the above license notices in your +# version of this file. +# +############################################################################## +# +# Load memalloc + +module="memalloc" +device="/dev/memalloc" +mode="666" + +echo + +if [ ! -e /dev ] +then + mkdir -p /dev/ +fi + +#insert module +rm_module=`lsmod |grep $module` +if [ ! -z "$rm_module" ] +then + rmmod $module || exit 1 +fi +insmod $module.ko $* || exit 1 + +echo "module $module inserted" + +#remove old nod +rm -f $device + +#read the major asigned at loading time +major=`cat /proc/devices | grep $module | cut -c1-3` + +echo "$module major = $major" + +#create dev node +mknod $device c $major 0 + +echo "node $device created" + +#give all 'rw' access +chmod $mode $device + +echo "set node access to $mode" + +#the end +echo diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/testbench_memalloc.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/testbench_memalloc.c new file mode 100644 index 000000000..1a570a551 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/memalloc/testbench_memalloc.c @@ -0,0 +1,207 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "basetype.h" +#include "memalloc.h" + +int main(void) { + + i32 memdev_fd = -1; + i32 memdev_fd_2 = -1; + i32 memdev_map = -1; + i32 i = 0, u = 0; + u32 *virtual1 = MAP_FAILED; + u32 *virtual2 = MAP_FAILED; + + u32 pgsize = getpagesize(); + + const char *memdev = "/dev/memalloc"; + const char *memmap = "/dev/mem"; + + u32 bus_address1 = 0, bus_address2 = 0; + u32 size = 4096; + + memdev_fd = open(memdev, O_RDWR); + if (memdev_fd == -1) { + printf("Failed to open dev: %s\n", memdev); + goto end1; + } + + memdev_fd_2 = open(memdev, O_RDWR); + if (memdev_fd_2 == -1) { + printf("Failed to open dev: %s\n", memdev); + goto end1; + } + + memdev_map = open(memmap, O_RDWR); + if (memdev_map == -1) { + printf("Failed to open dev: %s\n", memmap); + goto end1; + } + + size = (size + pgsize) & (~(pgsize - 1)); + + printf("Hard Reset\n", size); + /*ioctl(memdev_fd, MEMALLOC_IOCHARDRESET, 0);*/ + + for (i = 0; i < 100; i++) { + + ioctl(memdev_fd, MEMALLOC_IOCXGETBUFFER, &bus_address1); + printf("bus_address1 0x%08x\n", bus_address1); + + ioctl(memdev_fd_2, MEMALLOC_IOCXGETBUFFER, &bus_address2); + printf("bus_address2 0x%08x\n", bus_address2); + + /* test write stuff in the mem are*/ + + if (bus_address1) { + + virtual1 = (u32 *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, + memdev_map, bus_address1); + + printf("Virtual1 %08x\n", virtual1); + } + if (virtual1 != MAP_FAILED) { + for (u = 0; u < size / 4; u++) { + *virtual1 = i + 1; + } + } else { + printf("map failed\n"); + } + + /* test write stuff in the mem are*/ + + if (bus_address2) { + + virtual2 = (u32 *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, + memdev_map, bus_address2); + + printf("Virtual2 0x%08x\n", virtual2); + } + if (virtual2 != MAP_FAILED) { + for (u = 0; u < size / 4; u++) { + *virtual2 = i + 2; + } + } else { + printf("map failed\n"); + } + + if (virtual1 != MAP_FAILED) { + for (u = 0; u < size / 4; u++) { + if (*virtual1 != i + 1) { + printf("MISMATCH1!\n"); + break; + } + } + } + + if (virtual2 != MAP_FAILED) { + for (u = 0; u < size / 4; u++) { + if (*virtual2 != i + 2) { + printf("MISMATCH2!\n"); + break; + } + } + } + + if ((i % 30)) { + printf("release %d ", size); + ioctl(memdev_fd, MEMALLOC_IOCSFREEBUFFER, &bus_address1); + printf("address:\t\t0x%08x\n", bus_address1); + } + + printf("release %d ", size); + /*ioctl(memdev_fd_2, MEMALLOC_IOCSFREEBUFFER, &bus_address2); + printf("address:\t\t0x%08x\n", bus_address2); + virtual1 = MAP_FAILED; + virtual2 = MAP_FAILED;*/ + + if (!(i % 30)) { + close(memdev_fd); + memdev_fd = open(memdev, O_RDWR); + if (memdev_fd == -1) { + printf("Failed to open dev: %s\n", memdev); + goto end1; + } + } + close(memdev_fd_2); + memdev_fd_2 = open(memdev, O_RDWR); + if (memdev_fd_2 == -1) { + printf("Failed to open dev: %s\n", memdev); + goto end1; + } + } + +end1: + + close(memdev_fd); + close(memdev_fd_2); + close(memdev_map); + + return 0; +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/Makefile b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/Makefile new file mode 100644 index 000000000..21cec8ea1 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/Makefile @@ -0,0 +1,89 @@ +############################################################################# +# +# The MIT License (MIT) +# +# Copyright (c) 2014 - 2021 VERISILICON +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +############################################################################# +# +# The GPL License (GPL) +# +# Copyright (C) 2014 - 2021 VERISILICON +# +# 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. +# +############################################################################# +# +# Note: This software is released under dual MIT and GPL licenses. A +# recipient may use this file under the terms of either the MIT license or +# GPL License. If you wish to use only one license not the other, you can +# indicate your decision by deleting one of the above license notices in your +# version of this file. +# +############################################################################## + +DEBUG ?= n +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DHANTRODEC_DEBUG + DEBFLAGS += -DHANTROMMU_DEBUG +else + DEBFLAGS = -O2 +endif + +EXTRA_CFLAGS += $(DEBFLAGS) +# EXTRA_CFLAGS += -DDYNAMIC_MALLOC_VCMDNODE + +ifneq ($(KERNELRELEASE),) +# recursive call from kernel build system + +ccflags-y := -I$(src)/../dwl + +hantrodec-objs := hantro_dec.o hantro_mmu.o hantro_axife.o hantro_vcmd.o bidirect_list.o vcmdswhwregisters.o subsys.o kernel_allocator.o +obj-m += hantrodec.o + +else + +#KDIR := /mnt/export/Testing/Board_Version_Control/SW_Common/SOCLE_MDK-3D/openlinux/2.6.29/v0_5/android_linux-2.6.29 +KVER := $(shell uname -r) +KDIR := /lib/modules/$(KVER)/build + +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +endif + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + rm -rf modules.order Module.symvers diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/README b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/README new file mode 100644 index 000000000..323afc090 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/README @@ -0,0 +1,49 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. */ + +-- BUILD -- + +You need a fully configured kernel source tree in order to build the +driver. Please set the location of the kernel tree in the Makefile (KDIR). +If you want some extra debug information in the kernel logs, you could +define the HANTRODEC_DEBUG but please be aware that allot of things are traced +with this option. +Also you could set a particular device MAJOR in the 'hantrodec.c' if you don't want +dynamic allocation. + +Just run in this dir: + +%make + +-- USAGE -- + +The parameters that can be set when loading the driver are the HW IO base +address and the assigned IRQ number. + +First of all the module has to be inserted into the kernel with: +(you need a Linux shell cmd line) + +%insmod hantrodec.o base_port= irq= pcie= + +Set the correct values for the HW IO base address and the IRQ number if +the default values compiled into the module are not valid. + +E.g., to disable PCIE mode: + +%insmod hantrodec.o pcie=0 base_port= irq= + +E.g., to enable vcmd mode: + +%insmod hantrodec.o vcmd=1 + +Second of all a char device file has to be created: + +%mknod /dev/hantrodec c 0 + +Replace MAJOR with the correct value (i.e. read /proc/devices to found out +the exact values). + +Make sure that you have RW rights for the newly created dev file (use 'chmod'). + +The 'driver_load' script is provided for preparing all the things necessary for +the driver to be usable. The script is using 'awk' to retrieve the device's +major from /proc/devices. Remember to set the driver parameters. diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.c new file mode 100644 index 000000000..e7c4bc69a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.c @@ -0,0 +1,220 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#ifdef __FREERTOS__ +#include +#include "osal.h" +#elif defined(__linux__) +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +/* request_irq(), free_irq() */ +#include +#include + +#include +#include +/* needed for virt_to_phys() */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#else //For other os +//TODO... +#endif +#include "bidirect_list.h" + +void init_bi_list(bi_list* list) +{ + list->head = NULL; + list->tail = NULL; +} + +bi_list_node* bi_list_create_node(void) +{ + bi_list_node* node=NULL; + node=(bi_list_node*)vmalloc(sizeof(bi_list_node)); + if(node==NULL) + { + PDEBUG ("%s\n","vmalloc for node fail!"); + return node; + } + memset(node,0,sizeof(bi_list_node)); + return node; +} +void bi_list_free_node(bi_list_node* node) +{ + //free current node + vfree(node); + return; +} + +void bi_list_insert_node_tail(bi_list* list,bi_list_node* current_node) +{ + if(current_node==NULL) + { + PDEBUG ("%s\n","insert node tail NULL"); + return; + } + if(list->tail) + { + current_node->previous=list->tail; + list->tail->next=current_node; + list->tail=current_node; + list->tail->next=NULL; + } + else + { + list->head=current_node; + list->tail=current_node; + current_node->next=NULL; + current_node->previous=NULL; + } + return; +} + +void bi_list_insert_node_before(bi_list* list,bi_list_node* base_node,bi_list_node* new_node) +{ + bi_list_node* temp_node_previous=NULL; + if(new_node==NULL) + { + PDEBUG ("%s\n","insert node before new node NULL"); + return; + } + if(base_node) + { + if(base_node->previous) + { + //at middle position + temp_node_previous = base_node->previous; + temp_node_previous->next=new_node; + new_node->next = base_node; + base_node->previous = new_node; + new_node->previous=temp_node_previous; + } + else + { + //at head + base_node->previous = new_node; + new_node->next = base_node; + list->head=new_node; + new_node->previous = NULL; + } + } + else + { + //at tail + bi_list_insert_node_tail(list,new_node); + } + return; +} + + +void bi_list_remove_node(bi_list* list,bi_list_node* current_node) +{ + bi_list_node* temp_node_previous=NULL; + bi_list_node* temp_node_next=NULL; + if(current_node==NULL) + { + PDEBUG ("%s\n","remove node NULL"); + return; + } + temp_node_next=current_node->next; + temp_node_previous=current_node->previous; + + if(temp_node_next==NULL && temp_node_previous==NULL ) + { + //there is only one node. + list->head=NULL; + list->tail=NULL; + } + else if(temp_node_next==NULL) + { + //at tail + list->tail=temp_node_previous; + temp_node_previous->next=NULL; + } + else if( temp_node_previous==NULL) + { + //at head + list->head=temp_node_next; + temp_node_next->previous=NULL; + } + else + { + //at middle position + temp_node_previous->next=temp_node_next; + temp_node_next->previous=temp_node_previous; + } + return; +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.h new file mode 100644 index 000000000..1e9cdaabf --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/bidirect_list.h @@ -0,0 +1,107 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _BIDIRECT_LIST_H_ +#define _BIDIRECT_LIST_H_ +#ifdef __FREERTOS__ +#include "dev_common_freertos.h" /* needed for the _IOW etc stuff used later */ +#elif defined(__linux__) +#include /* needed for the _IOW etc stuff used later */ +#else //For other os +//TODO... +#endif + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef BIDIRECTION_LIST_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hmp4e: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(__FILE__ ":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/***********************************************************************************************************************************************\ +* +\**********************************************************************************************************************************************/ +typedef struct bi_list_node{ + void* data; + struct bi_list_node* next; + struct bi_list_node* previous; +}bi_list_node; +typedef struct bi_list{ + bi_list_node* head; + bi_list_node* tail; +}bi_list; + +void init_bi_list(bi_list* list); + +bi_list_node* bi_list_create_node(void); + +void bi_list_free_node(bi_list_node* node); + +void bi_list_insert_node_tail(bi_list* list,bi_list_node* current_node); + +void bi_list_insert_node_before(bi_list* list,bi_list_node* base_node,bi_list_node* new_node); + +void bi_list_remove_node(bi_list* list,bi_list_node* current_node); + +#endif /* !_BIDIRECT_LIST_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_mpcore.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_mpcore.sh new file mode 100644 index 000000000..478d2cf6f --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_mpcore.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +KDIR=/mnt/export/Testing/Board_Version_Control/SW_Common/ARM_realview_v6/2.6.28-arm1/v0_1-v6/linux-2.6.28-arm1 + +echo +echo "KDIR=$KDIR" +echo "CROSS=$CROSS" + +make KDIR=$KDIR $1 + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_socle.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_socle.sh new file mode 100644 index 000000000..8997658ac --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_socle.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +KDIR=/mnt/export/Testing/Board_Version_Control/SW_Common/SOCLE_MDK-3D/openlinux/2.6.29/v0_5/android_linux-2.6.29 + +echo +echo "KDIR=$KDIR" +echo "CROSS=$CROSS" + +make KDIR=$KDIR $1 + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_vexpress.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_vexpress.sh new file mode 100644 index 000000000..583babff2 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/build_for_vexpress.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +KDIR=/mnt/export/Testing/Board_Version_Control/SW_Common/VExpress/linux-linaro-3.2-2012.01-0 + +echo +echo "KDIR=$KDIR" +echo "CROSS=$CROSS" + +make KDIR=$KDIR $1 + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load.sh new file mode 100644 index 000000000..54d422870 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load.sh @@ -0,0 +1,43 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +module="hantrodec" +device="/dev/hantrodec" +mode="666" + +echo + +if [ ! -e /dev ] +then + mkdir -p /dev/ +fi + +#insert module +rm_module=`lsmod |grep $module` +if [ ! -z "$rm_module" ] +then + rmmod $module || exit 1 +fi +insmod $module.ko $* || exit 1 + +echo "module $module inserted" + +#remove old nod +rm -f $device + +#read the major asigned at loading time +major=`cat /proc/devices | grep $module | cut -c1-3` + +echo "$module major = $major" + +#create dev node +mknod $device c $major 0 + +echo "node $device created" + +#give all 'rw' access +chmod $mode $device + +echo "set node access to $mode" + +#the end +echo diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load_sc.sh b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load_sc.sh new file mode 100644 index 000000000..92491fb7d --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/driver_load_sc.sh @@ -0,0 +1,43 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +module="hantrodec" +device="/dev/hantrodec" +mode="666" + +echo + +if [ ! -e /dev ] +then + mkdir -p /dev/ +fi + +#insert module +rm_module=`lsmod |grep $module` +if [ ! -z "$rm_module" ] +then + rmmod $module || exit 1 +fi +insmod $module.ko $* base_port=0xd0000000 || exit 1 + +echo "module $module inserted" + +#remove old nod +rm -f $device + +#read the major asigned at loading time +major=`cat /proc/devices | grep $module | cut -c1-3` +#major='50' +echo "$module major = $major" + +#create dev node +mknod $device c $major 0 + +echo "node $device created" + +#give all 'rw' access +chmod $mode $device + +echo "set node access to $mode" + +#the end +echo diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/dev_common_freertos.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/dev_common_freertos.h new file mode 100644 index 000000000..c88c996ef --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/dev_common_freertos.h @@ -0,0 +1,255 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#ifndef _DEV_COMMON_FREERTOS_H_ +#define _DEV_COMMON_FREERTOS_H_ + +/**********Driver for FreeRTOS based on the method of Linux's calling**********/ +#define __u8 u8 +#define __i8 i8 +#define __u32 u32 +#define __i32 i32 + +#define sema_init(s,v) sem_init(s,0,v) + +//PCIE +#define pci_get_device(a,b,c) (void *)0 +#define pci_enable_device(a) 0 +#define pci_disable_device(a) 0 +#define pci_resource_start(a,b) 0 +#define pci_resource_len(a,b) 0 +/*--------------------------------*/ +/* IO */ +//#define O_RDONLY (1) +//#define O_WRONLY (1 << 1) +//#define O_RDWR (O_RDONLY | O_WRONLY) +//#define O_SYNC (1 << 2) +#undef MAP_FAILED +#define MAP_FAILED NULL +#undef NULL +#define NULL ((void *)0) +#define getpagesize() (1) +#define ioremap_nocache(addr,size) (addr) +#define iounmap(addr) +#define mmap(va, size, access, flag, fd, pa) DirectMemoryMap(pa, size) +#define munmap(pRegs, size) +#define vmalloc(a) pvPortMalloc(a) //malloc(a); +#define vfree(a) vPortFree(a) //free(a); +#define probe(a) Platform_init(a) +#define open(name,flag) freertos_open(name,flag) +#define ioctl(fd,cmd,arg) freertos_ioctl(fd,cmd,arg) +#define close(fd) freertos_close(fd) +#define KERN_INFO +#define KERN_ERR +#define KERN_WARNING +#define KERN_DEBUG +#define printk PDEBUG +#define access_ok(a,b,c) (1) +#define __init +#define __exit +#define msleep(s) osal_usleep(s*1000) + +/* + * Let any architecture override either of the following before + * including this file. + */ +#ifndef _IOC_SIZEBITS +# define _IOC_SIZEBITS 14 +#endif + +#ifndef _IOC_DIRBITS +# define _IOC_DIRBITS 2 +#endif +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + + /* + * Direction bits, which any architecture can choose to override + * before including this file. + */ +#ifndef _IOC_NONE +# define _IOC_NONE 0U +#endif + +#ifndef _IOC_WRITE +# define _IOC_WRITE 1U +#endif + +#ifndef _IOC_READ +# define _IOC_READ 2U +#endif + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +#define _IOC_TYPECHECK(t) (sizeof(t)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) +#define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode ioctl numbers.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* ...and for the drivers/sound files... */ +#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#define DECLARE_WAIT_QUEUE_HEAD(a) + +/* Kernel/User space */ +#define copy_from_user(des,src,size) (memcpy(des,src,size),0) +#define copy_to_user(des,src,size) (memcpy(des,src,size),0) +#define raw_copy_from_user(des,src,size) (memcpy(des,src,size),0) +#define raw_copy_to_user(des,src,size) (memcpy(des,src,size),0) +#define __put_user(val,user) (*(user) = (val)) +#define __get_user(val,user) ((val) = *user) + +/* Interrupt */ +/*********************request_irq, disable_irq, enable_irq need to be provided by customer*********************/ +#define request_irq(i,isr,flag,name,data) RegisterIRQ(i, isr, flag, name, data) +#define disable_irq(i) IntDisableIRQ(i) +#define enable_irq(i) IntEnableIRQ(i) +#define free_irq(i,data) +#define irqreturn_t void +#define down_interruptible(a) pthread_mutex_lock(a) +#define up(a) pthread_mutex_unlock(a) +#define wait_event_interruptible(a,b) sem_wait(&a) +#define wake_up_interruptible_all(a) sem_post(a) +#define IRQ_RETVAL(a) //(a) +#define IRQ_HANDLED //1 +#define IRQ_NONE //0 +#define register_chrdev(m,name,op) (0) +#define unregister_chrdev(m,name) +#define IRQF_SHARED 1 +#define IRQF_DISABLED 0x00000020 +#define request_mem_region(addr,size,name) (1) +#define release_mem_region(addr,size) +#define kill_fasync(queue,sig,flag) +//Timer +//typedef TimerHandle_t struct timer_list; //For FreeRTOS + +#define ERR_OS_FAIL (0xffff) +#define ERESTARTSYS ERR_OS_FAIL +#ifdef EFAULT +#undef EFAULT +#endif +#define EFAULT ERR_OS_FAIL +#ifdef ENOTTY +#undef ENOTTY +#endif +#define ENOTTY ERR_OS_FAIL +#ifdef EINVAL +#undef EINVAL +#endif +#define EINVAL ERR_OS_FAIL +#ifdef EBUSY +#undef EBUSY +#endif +#define EBUSY ERR_OS_FAIL + +/* kernel sync objects */ +/**********the atomic operations need to be provided by customer**********/ +//#define atomic_t __attribute__((section("cpu_dram"), aligned(4))) xmp_atomic_int_t//i32 +//#define ATOMIC_INIT(a) XMP_ATOMIC_INT_INITIALIZER(a)//a +//#define atomic_inc(a) xmp_atomic_int_increment(a,1)//((*(a))++) +//#define atomic_read(a) xmp_atomic_int_value(a)//(a) +#define atomic_t i32 +#define ATOMIC_INIT(a) a +#define atomic_inc(a) ((*(a))++) +#define atomic_read(a) (a) +typedef int sig_atomic_t; +//typedef int sigset_t; +#define sigemptyset(set) +#define sigaddset(set, sig) +#define sigsuspend(set) +#define sigwait(set, signo) + +#define spinlock_t pthread_mutex_t +#define spin_lock_init(a) (*a = PTHREAD_MUTEX_INITIALIZER) +#define isr_spin_lock_irqsave(a,b) +#define isr_spin_unlock_irqrestore(a,b) +#define spin_lock_irqsave(a,b) {pthread_mutex_lock(a); \ + b = ioread32((void *)SYS_REG_INT_EN) & g_vc8000_int_enable_mask; \ + *((volatile uint32_t *)SYS_REG_INT_EN) &= ~(b);} +#define spin_unlock_irqrestore(a,b) {*((volatile uint32_t *)SYS_REG_INT_EN) = \ + ioread32((void *)SYS_REG_INT_EN) | (b); \ + pthread_mutex_unlock(a); } +#define spin_lock(a) pthread_mutex_lock(a) +#define spin_unlock(a) pthread_mutex_unlock(a) +#define getpid() (int)pthread_self() + +#endif /* _DEV_COMMON_FREERTOS_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_dec_freertos.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_dec_freertos.c new file mode 100644 index 000000000..59adab5e2 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_dec_freertos.c @@ -0,0 +1,2065 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#include "hantrodec.h" +#include "dwl_defs.h" +#include "subsys.h" +#include "io_tools.h" +#include "osal.h" + +#include "xtensa_api.h" +//#include "../src/libxmp/xmp-library.h" //For atomic operations +#include //interrupt for xtensa + +#include +#include + +#undef PDEBUG +#ifdef HANTRODEC_DEBUG +# ifdef __KERNEL__ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hantrodec: " fmt, ## args) +# else +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, ...) +#endif +/*----------------------------------------------------------------------------------------- +************************CPU Xtensa OS FreeRTOS PORTING LAYER******************************* +------------------------------------------------------------------------------------------*/ +static u32 g_vc8000_int_enable_mask = 0; +//VCMD for netint +#define SYS_REG_INT_TOP_BASE (0x02800000) +//ENCODER +#if 0 +#define CPU_INT_IRQ 8 /* All Encoder Modules' interrupt will be connected to CPU IRQ 8 */ +#define SYS_INT_MASK (0x220) +#define SYS_REG_INT_VAL (SYS_REG_INT_TOP_BASE + 0x3c) +#define SYS_REG_INT_STAT (SYS_REG_INT_TOP_BASE + 0x40) +#define SYS_REG_INT_EN (SYS_REG_INT_TOP_BASE + 0x44) +#else +//DECODER +#define CPU_INT_IRQ 9 +#define SYS_INT_MASK (0x001) +#define SYS_REG_INT_VAL (SYS_REG_INT_TOP_BASE + 0x48) +#define SYS_REG_INT_STAT (SYS_REG_INT_TOP_BASE + 0x4c) +#define SYS_REG_INT_EN (SYS_REG_INT_TOP_BASE + 0x50) +#endif + +/* hantro G1 regs config including dec and pp */ +#define HANTRO_DEC_ORG_REGS 60 +#define HANTRO_PP_ORG_REGS 41 + +#define HANTRO_DEC_EXT_REGS 27 +#define HANTRO_PP_EXT_REGS 9 + +//#define HANTRO_G1_DEC_TOTAL_REGS (HANTRO_DEC_ORG_REGS + HANTRO_DEC_EXT_REGS) +#define HANTRO_PP_TOTAL_REGS (HANTRO_PP_ORG_REGS + HANTRO_PP_EXT_REGS) +#define HANTRO_G1_DEC_REGS 155 /*G1 total regs*/ + +//#define HANTRO_DEC_ORG_FIRST_REG 0 +//#define HANTRO_DEC_ORG_LAST_REG 59 +//#define HANTRO_DEC_EXT_FIRST_REG 119 +//#define HANTRO_DEC_EXT_LAST_REG 145 + +#define HANTRO_PP_ORG_FIRST_REG 60 +#define HANTRO_PP_ORG_LAST_REG 100 +#define HANTRO_PP_EXT_FIRST_REG 146 +#define HANTRO_PP_EXT_LAST_REG 154 + +/* hantro G2 reg config */ +#define HANTRO_G2_DEC_REGS 337 /*G2 total regs*/ +#define HANTRO_G2_DEC_FIRST_REG 0 +#define HANTRO_G2_DEC_LAST_REG HANTRO_G2_DEC_REGS-1 + +/* hantro VC8000D reg config */ +#define HANTRO_VC8000D_REGS 503 /*VC8000D total regs*/ +#define HANTRO_VC8000D_FIRST_REG 0 +#define HANTRO_VC8000D_LAST_REG HANTRO_VC8000D_REGS-1 + +/* Logic module IRQs */ +#define HXDEC_NO_IRQ -1 + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define DEC_IO_SIZE_MAX (MAX(MAX(HANTRO_G2_DEC_REGS, HANTRO_G1_DEC_REGS), HANTRO_VC8000D_REGS) * 4) + +/* User should modify these configuration if do porting to own platform. */ +/* Please guarantee the base_addr, io_size, dec_irq belong to same core. */ + +/* Defines use kernel clk cfg or not**/ +//#define CLK_CFG +#if 0 +#ifdef CLK_CFG +#define CLK_ID "hantrodec_clk" /*this id should conform with platform define*/ +#endif +#endif + +/* Logic module base address */ +#define SOCLE_LOGIC_0_BASE (0x02310000 + 0x2000) +#define SOCLE_LOGIC_1_BASE -1 //(0x02350000 + 0x2000) +#define SOCLE_LOGIC_2_BASE -1 //(0x02390000 + 0x2000) +#define SOCLE_LOGIC_3_BASE -1 //(0x023D0000 + 0x2000) + +//#define VEXPRESS_LOGIC_0_BASE 0xFC010000 +//#define VEXPRESS_LOGIC_1_BASE 0xFC020000 + +#define DEC_IO_SIZE_0 DEC_IO_SIZE_MAX /* bytes */ +#define DEC_IO_SIZE_1 DEC_IO_SIZE_MAX /* bytes */ +#define DEC_IO_SIZE_2 DEC_IO_SIZE_MAX /* bytes */ +#define DEC_IO_SIZE_3 DEC_IO_SIZE_MAX /* bytes */ + +#define DEC_IRQ_0 9 //3 +#define DEC_IRQ_1 HXDEC_NO_IRQ + +#define IS_G1(hw_id) (((hw_id) == 0x6731)? 1 : 0) +#define IS_G2(hw_id) (((hw_id) == 0x6732)? 1 : 0) +#define IS_VC8000D(hw_id) (((hw_id) == 0x8001)? 1 : 0) + +static const int DecHwId[] = { + 0x6731, /* G1 */ + 0x6732, /* G2 */ + 0x8001 /* VC8000D */ +}; + +unsigned long base_port = 0; +volatile unsigned char *reg = NULL; +int vcmd = 0; + +unsigned long multicorebase[HXDEC_MAX_CORES] = { + SOCLE_LOGIC_0_BASE, + SOCLE_LOGIC_1_BASE, + SOCLE_LOGIC_2_BASE, + SOCLE_LOGIC_3_BASE +}; + +int irq[HXDEC_MAX_CORES] = { + DEC_IRQ_0, + -1, + -1, + -1 +}; + +unsigned int iosize[HXDEC_MAX_CORES] = { + DEC_IO_SIZE_0, + DEC_IO_SIZE_1, + DEC_IO_SIZE_2, + DEC_IO_SIZE_3 +}; + +/* Because one core may contain multi-pipeline, so multicore base may be changed */ +unsigned long multicorebase_actual[HXDEC_MAX_CORES]; + +struct subsys_config vpu_subsys[MAX_SUBSYS_NUM]; + +int elements = 4; + +#if 0 +#ifdef CLK_CFG +struct clk *clk_cfg; +int is_clk_on; +struct timer_list timer; +#endif +#endif + +static int hantrodec_major = 0; /* dynamic allocation */ + +/* here's all the must remember stuff */ +typedef struct { + char *buffer; + volatile unsigned int iosize[HXDEC_MAX_CORES]; + volatile u8 *hwregs[HXDEC_MAX_CORES]; + volatile int irq[HXDEC_MAX_CORES]; + int hw_id[HXDEC_MAX_CORES]; + int cores; + //struct fasync_struct *async_queue_dec; + //struct fasync_struct *async_queue_pp; +} hantrodec_t; + +typedef struct { + u32 cfg[HXDEC_MAX_CORES]; /* indicate the supported format */ + u32 cfg_backup[HXDEC_MAX_CORES]; /* back up of cfg */ + int its_main_core_id[HXDEC_MAX_CORES]; /* indicate if main core exist */ + int its_aux_core_id[HXDEC_MAX_CORES]; /* indicate if aux core exist */ +} core_cfg; + +static hantrodec_t hantrodec_data; /* dynamic allocation? */ + +static int ReserveIO(void); +static void ReleaseIO(void); + +static void ResetAsic(hantrodec_t * dev); + +#ifdef HANTRODEC_DEBUG +static void dump_regs(hantrodec_t *dev); +#endif + +/* Interrupt */ +/*********************request_irq, disable_irq, enable_irq need to be provided by customer*********************/ +static int RegisterIRQ(i32 i, IRQHandler isr, i32 flag, const char* name, void *data); +static void IntEnableIRQ(u32 irq); +static void IntDisableIRQ(u32 irq); +static void IntClearIRQStatus(u32 irq, u32 type); +static u32 IntGetIRQStatus(u32 irq); +static inline uint32_t ReadInterruptStatus(void); + +/* IRQ handler */ +static irqreturn_t hantrodec_isr(void *args); +static u32 dec_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX/4]; + +#undef down_interruptible +#undef up +#define down_interruptible(a) sem_wait(a) +#define up(a) sem_post(a) +sem_t dec_core_sem; +sem_t pp_core_sem; + +static int dec_irq = 0; +static int pp_irq = 0; + +/*********************the atomic operations need to be provided by customer*********************/ +atomic_t irq_rx = ATOMIC_INIT(0); +atomic_t irq_tx = ATOMIC_INIT(0); + +static int dec_owner[HXDEC_MAX_CORES]; +static int pp_owner[HXDEC_MAX_CORES]; +static int CoreHasFormat(const u32 *cfg, int core, u32 format); +#if 0 +/* spinlock_t owner_lock = SPIN_LOCK_UNLOCKED; */ +DEFINE_SPINLOCK(owner_lock); +DECLARE_WAIT_QUEUE_HEAD(dec_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(pp_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(hw_queue); + +#ifdef CLK_CFG +DEFINE_SPINLOCK(clk_lock); +#endif +#endif + +pthread_mutex_t owner_lock = PTHREAD_MUTEX_INITIALIZER; +sem_t dec_wait_queue; +sem_t pp_wait_queue; +sem_t hw_queue; + +#define DWL_CLIENT_TYPE_H264_DEC 1U +#define DWL_CLIENT_TYPE_MPEG4_DEC 2U +#define DWL_CLIENT_TYPE_JPEG_DEC 3U +#define DWL_CLIENT_TYPE_PP 4U +#define DWL_CLIENT_TYPE_VC1_DEC 5U +#define DWL_CLIENT_TYPE_MPEG2_DEC 6U +#define DWL_CLIENT_TYPE_VP6_DEC 7U +#define DWL_CLIENT_TYPE_AVS_DEC 8U +#define DWL_CLIENT_TYPE_RV_DEC 9U +#define DWL_CLIENT_TYPE_VP8_DEC 10U +#define DWL_CLIENT_TYPE_VP9_DEC 11U +#define DWL_CLIENT_TYPE_HEVC_DEC 12U + +static core_cfg config; + +static void ReadCoreConfig(hantrodec_t *dev) { + int c; + u32 reg, tmp, mask; + + memset(config.cfg, 0, sizeof(config.cfg)); + + for(c = 0; c < dev->cores; c++) { + /* Decoder configuration */ + if (IS_G1(dev->hw_id[c])) { + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_H264_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has H264\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_JPEG_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has JPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_HJPEG_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has HJPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_MPEG4_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has MPEG4\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0; + + tmp = (reg >> DWL_VC1_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VC1\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC: 0; + + tmp = (reg >> DWL_MPEG2_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has MPEG2\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0; + + tmp = (reg >> DWL_VP6_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VP6\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG_2 * 4)); + + /* VP7 and WEBP is part of VP8 */ + mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E); + tmp = (reg & mask); + if(tmp & (1 << DWL_VP8_E)) + printk(KERN_INFO "hantrodec: core[%d] has VP8\n", c); + if(tmp & (1 << DWL_VP7_E)) + printk(KERN_INFO "hantrodec: core[%d] has VP7\n", c); + if(tmp & (1 << DWL_WEBP_E)) + printk(KERN_INFO "hantrodec: core[%d] has WebP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0; + + tmp = (reg >> DWL_AVS_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has AVS\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC: 0; + + tmp = (reg >> DWL_RV_E) & 0x03U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has RV\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c] + HANTROPP_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_G1_PP_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + } else if((IS_G2(dev->hw_id[c]))) { + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_CFG_STAT * 4)); + + tmp = (reg >> DWL_G2_HEVC_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has HEVC\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0; + + tmp = (reg >> DWL_G2_VP9_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VP9\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c] + HANTRODECPP_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_G2_PP_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + } else if((IS_VC8000D(dev->hw_id[c])) && config.its_main_core_id[c] < 0) { + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_H264_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has H264\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_H264HIGH10_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has H264HIGH10\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_JPEG_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has JPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_HJPEG_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has HJPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_MPEG4_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has MPEG4\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0; + + tmp = (reg >> DWL_VC1_E) & 0x3U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VC1\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC: 0; + + tmp = (reg >> DWL_MPEG2_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has MPEG2\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0; + + tmp = (reg >> DWL_VP6_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VP6\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG_2 * 4)); + + /* VP7 and WEBP is part of VP8 */ + mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E); + tmp = (reg & mask); + if(tmp & (1 << DWL_VP8_E)) + printk(KERN_INFO "hantrodec: core[%d] has VP8\n", c); + if(tmp & (1 << DWL_VP7_E)) + printk(KERN_INFO "hantrodec: core[%d] has VP7\n", c); + if(tmp & (1 << DWL_WEBP_E)) + printk(KERN_INFO "hantrodec: core[%d] has WebP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0; + + tmp = (reg >> DWL_AVS_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has AVS\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC: 0; + + tmp = (reg >> DWL_RV_E) & 0x03U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has RV\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG_3 * 4)); + + tmp = (reg >> DWL_HEVC_E) & 0x07U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has HEVC\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0; + + tmp = (reg >> DWL_VP9_E) & 0x07U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has VP9\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c] + HANTRODECPP_CFG_STAT * 4)); + + tmp = (reg >> DWL_PP_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + + if (config.its_aux_core_id[c] >= 0) { + /* set main_core_id and aux_core_id */ + reg = ioread32((void*)(dev->hwregs[c] + HANTRODEC_SYNTH_CFG_2 * 4)); + + tmp = (reg >> DWL_H264_PIPELINE_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has pipeline H264\n", c); + config.cfg[config.its_aux_core_id[c]] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_JPEG_PIPELINE_E) & 0x01U; + if(tmp) printk(KERN_INFO "hantrodec: core[%d] has pipeline JPEG\n", c); + config.cfg[config.its_aux_core_id[c]] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + } + } + } + memcpy(config.cfg_backup, config.cfg, sizeof(config.cfg)); +} + +static int CoreHasFormat(const u32 *cfg, int core, u32 format) { + return (cfg[core] & (1 << format)) ? 1 : 0; +} + +int GetDecCore(long core, hantrodec_t *dev, int filp, unsigned long format) { + int success = 0; + unsigned long flags; + + spin_lock_irqsave(&owner_lock, flags); + if(CoreHasFormat(config.cfg, core, format) && dec_owner[core] == 0 /*&& config.its_main_core_id[core] >= 0*/) { + dec_owner[core] = filp; + success = 1; + + /* If one main core takes one format which doesn't supported by aux core, set aux core's cfg to none video format support */ + if (config.its_aux_core_id[core] >= 0 && + !CoreHasFormat(config.cfg, config.its_aux_core_id[core], format)) { + config.cfg[config.its_aux_core_id[core]] = 0; + } + /* If one aux core takes one format, set main core's cfg to aux core supported video format */ + else if (config.its_main_core_id[core] >= 0) { + config.cfg[config.its_main_core_id[core]] = config.cfg[core]; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return success; +} + +int GetDecCoreAny(long *core, hantrodec_t *dev, int filp, + unsigned long format) { + int success = 0; + long c; + + *core = -1; + + for(c = 0; c < dev->cores; c++) { + /* a free core that has format */ + /* xuanji: simplely impto get core for single core, exists issue */ + if(GetDecCore(c, dev, filp, format)) { + success = 1; + *core = c; + break; + } + } + + return success; +} + +int GetDecCoreID(hantrodec_t *dev, int filp, + unsigned long format) { + long c; + unsigned long flags; + + int core_id = -1; + + for(c = 0; c < dev->cores; c++) { + /* a core that has format */ + spin_lock_irqsave(&owner_lock, flags); + if(CoreHasFormat(config.cfg, c, format)) { + core_id = c; + spin_unlock_irqrestore(&owner_lock, flags); + break; + } + spin_unlock_irqrestore(&owner_lock, flags); + } + return core_id; +} + +#if 0 +static int hantrodec_choose_core(int is_g1) { + volatile unsigned char *reg = NULL; + unsigned int blk_base = 0x38320000; + + PDEBUG("hantrodec_choose_core\n"); + if (!request_mem_region(blk_base, 0x1000, "blk_ctl")) { + printk(KERN_INFO "blk_ctl: failed to reserve HW regs\n"); + return -EBUSY; + } + + reg = (volatile u8 *) ioremap_nocache(blk_base, 0x1000); + + if (reg == NULL ) { + printk(KERN_INFO "blk_ctl: failed to ioremap HW regs\n"); + if (reg) + iounmap((void *)reg); + release_mem_region(blk_base, 0x1000); + return -EBUSY; + } + + // G1 use, set to 1; G2 use, set to 0, choose the one you are using + if (is_g1) + iowrite32(0x1, (void*)(reg + 0x14)); // VPUMIX only use G1, user should modify the reg according to platform design + else + iowrite32(0x0, (void*)(reg + 0x14)); // VPUMIX only use G2, user should modify the reg according to platform design + + if (reg) + iounmap((void *)reg); + release_mem_region(blk_base, 0x1000); + PDEBUG("hantrodec_choose_core OK!\n"); + return 0; +} +#endif + +long ReserveDecoder(hantrodec_t *dev, int filp, unsigned long format) { + long core = -1; + + /* reserve a core */ + if (down_interruptible(&dec_core_sem)) + return -ERESTARTSYS; + + /* lock a core that has specific format*/ + if(wait_event_interruptible(hw_queue,GetDecCoreAny(&core, dev, filp, format) != 0 )) + return -ERESTARTSYS; + /* xuanji: simplely imp to get core for single core, exists issue */ + GetDecCoreAny(&core, dev, filp, format); + +#if 0 + if(IS_G1(dev->hw_id[core])) { + if (0 == hantrodec_choose_core(1)) + printk("G1 is reserved\n"); + else + return -1; + } else { + if (0 == hantrodec_choose_core(0)) + printk("G2 is reserved\n"); + else + return -1; + } +#endif + + return core; +} + +void ReleaseDecoder(hantrodec_t *dev, long core) { + u32 status; + unsigned long flags; + + status = ioread32((void*)(dev->hwregs[core] + HANTRODEC_IRQ_STAT_DEC_OFF)); + + /* make sure HW is disabled */ + if(status & HANTRODEC_DEC_E) { + printk(KERN_INFO "hantrodec: DEC[%li] still enabled -> reset\n", core); + + /* abort decoder */ + status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void*)(dev->hwregs[core] + HANTRODEC_IRQ_STAT_DEC_OFF)); + } + + spin_lock_irqsave(&owner_lock, flags); + + /* If aux core released, revert main core's config back */ + if (config.its_main_core_id[core] >= 0) { + config.cfg[config.its_main_core_id[core]] = config.cfg_backup[config.its_main_core_id[core]]; + } + + /* If main core released, revert aux core's config back */ + if (config.its_aux_core_id[core] >= 0) { + config.cfg[config.its_aux_core_id[core]] = config.cfg_backup[config.its_aux_core_id[core]]; + } + + dec_owner[core] = 0; + + spin_unlock_irqrestore(&owner_lock, flags); + + up(&dec_core_sem); + + wake_up_interruptible_all(&hw_queue); +} + +long ReservePostProcessor(hantrodec_t *dev, int filp) { + unsigned long flags; + + long core = 0; + + /* single core PP only */ + if (down_interruptible(&pp_core_sem)) + return -ERESTARTSYS; + + spin_lock_irqsave(&owner_lock, flags); + + pp_owner[core] = filp; + + spin_unlock_irqrestore(&owner_lock, flags); + + return core; +} + +void ReleasePostProcessor(hantrodec_t *dev, long core) { + unsigned long flags; + + u32 status = ioread32((void*)(dev->hwregs[core] + HANTRO_IRQ_STAT_PP_OFF)); + + /* make sure HW is disabled */ + if(status & HANTRO_PP_E) { + printk(KERN_INFO "hantrodec: PP[%li] still enabled -> reset\n", core); + + /* disable IRQ */ + status |= HANTRO_PP_IRQ_DISABLE; + + /* disable postprocessor */ + status &= (~HANTRO_PP_E); + iowrite32(0x10, (void*)(dev->hwregs[core] + HANTRO_IRQ_STAT_PP_OFF)); + } + + spin_lock_irqsave(&owner_lock, flags); + + pp_owner[core] = 0; + + spin_unlock_irqrestore(&owner_lock, flags); + + up(&pp_core_sem); +} + +long ReserveDecPp(hantrodec_t *dev, int filp, unsigned long format) { + /* reserve core 0, DEC+PP for pipeline */ + unsigned long flags; + + long core = 0; + + /* check that core has the requested dec format */ + if(!CoreHasFormat(config.cfg, core, format)) + return -EFAULT; + + /* check that core has PP */ + if(!CoreHasFormat(config.cfg, core, DWL_CLIENT_TYPE_PP)) + return -EFAULT; + + /* reserve a core */ + if (down_interruptible(&dec_core_sem)) + return -ERESTARTSYS; + + /* wait until the core is available */ + if(wait_event_interruptible(hw_queue, GetDecCore(core, dev, filp, format) != 0)) { + up(&dec_core_sem); + return -ERESTARTSYS; + } + /* xuanji: simplely imp to get core for single core, exists issue */ + GetDecCore(core, dev, filp, format); + + if (down_interruptible(&pp_core_sem)) { + ReleaseDecoder(dev, core); + return -ERESTARTSYS; + } + + spin_lock_irqsave(&owner_lock, flags); + pp_owner[core] = filp; + spin_unlock_irqrestore(&owner_lock, flags); + + return core; +} + +long DecFlushRegs(hantrodec_t *dev, struct core_desc *core) { + long ret = 0, i; + + u32 id = core->id; + +#if 1 + ret = copy_from_user(dec_regs[id], core->regs, HANTRO_VC8000D_REGS*4); + if (ret) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + /* write all regs but the status reg[1] to hardware */ + iowrite32(0x0, (void*)(dev->hwregs[id] + 4)); + for(i = 2; i <= HANTRO_VC8000D_LAST_REG; i++) { + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + } +#else + if (IS_G1(dev->hw_id[id])) { + /* copy original dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id], core->regs, HANTRO_DEC_ORG_REGS*4); + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } +#ifdef USE_64BIT_ENV + /* copy extended dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id] + HANTRO_DEC_EXT_FIRST_REG, + core->regs + HANTRO_DEC_EXT_FIRST_REG, + HANTRO_DEC_EXT_REGS*4); +#endif + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + /* write dec regs but the status reg[1] to hardware */ + /* both original and extended regs need to be written */ + for(i = 2; i <= HANTRO_DEC_ORG_LAST_REG; i++) { + iowrite32(dec_regs[id][i], dev->hwregs[id] + i*4); + } +#ifdef USE_64BIT_ENV + for(i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) + iowrite32(dec_regs[id][i], dev->hwregs[id] + i*4); +#endif + } else { + ret = copy_from_user(dec_regs[id], core->regs, HANTRO_G2_DEC_REGS*4); + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + /* write all regs but the status reg[1] to hardware */ + for(i = 2; i <= HANTRO_G2_DEC_LAST_REG; i++) { +#if 0 + if(i==2) + //dec_regs[id][i] = 0x78777777; + //dec_regs[id][i] = 0x78778787; + //c_regs[id][i] = 0x78888888; //64bit + { + printk("reg2=%08x\n",dec_regs[id][i]); + //dec_regs[id][i] = 0xF0F00000;//128bit 0xF0F0F000 + } + //dec_regs[id][i] = 0xF0F0000F;//128bit 0xF0F00000 for big endian + if(i==58) { + printk("reg58=%08x\n",dec_regs[id][i]); + //dec_regs[id][i]= 0x210;//128bit + //dec_regs[id][i]= 0x110;//64bit + } + if(i==3) { + printk("reg3=%08x\n",dec_regs[id][i]); + //dec_regs[id][i] |= 0x00F00000;//128bit + //dec_regs[id][i]= 0x110;//64bit + } +#endif + iowrite32(dec_regs[id][i], dev->hwregs[id] + i*4); + + } + } +#endif + + /* write the status register, which may start the decoder */ + iowrite32(dec_regs[id][1], (void*)(dev->hwregs[id] + 4)); + + PDEBUG("Hantrodec flushed registers on core %d\n", id); + + return 0; +} + + +long DecWriteRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret = 0, i; + u32 id = core->id; + i = core->reg_id; + + ret = copy_from_user(dec_regs[id] + core->reg_id, core->regs + core->reg_id, 4); + + if (ret) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + return 0; +} + +long DecReadRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret, i; + u32 id = core->id; + i = core->reg_id; + + /* user has to know exactly what they are asking for */ + //if(core->size != (HANTRO_VC8000D_REGS * 4)) + // return -EFAULT; + + /* read specific registers from hardware */ + i = core->reg_id; + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + + /* put registers to user space*/ + ret = copy_to_user(core->regs + core->reg_id, dec_regs[id] + core->reg_id, 4); + if (ret) { + PDEBUG("Hantrodec copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + return 0; +} + +long DecRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + long ret, i; + u32 id = core->id; + +#if 1 + //for(i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++) { + for(i = 0; i <= HANTRO_VC8000D_LAST_REG; i++) { + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + } + //ret = copy_to_user(core->regs, dec_regs[id], HANTRO_DEC_ORG_REGS*4); + ret = copy_to_user(core->regs, dec_regs[id], HANTRO_G2_DEC_REGS*4); + if (ret) { + PDEBUG("Hantrodec copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } +#else + + if (IS_G1(dev->hw_id[id])) { +#ifdef USE_64BIT_ENV + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_DEC_TOTAL_REGS * 4)) + return -EFAULT; +#else + /* user has to know exactly what they are asking for */ + //if(core->size != (HANTRO_DEC_ORG_REGS * 4)) + // return -EFAULT; +#endif + /* read all registers from hardware */ + /* both original and extended regs need to be read */ + for(i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++) + dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4); +#ifdef USE_64BIT_ENV + for(i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) + dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4); +#endif + /* put registers to user space*/ + /* put original registers to user space*/ + ret = copy_to_user(core->regs, dec_regs[id], HANTRO_DEC_ORG_REGS*4); +#ifdef USE_64BIT_ENV + /*put extended registers to user space*/ + ret = copy_to_user(core->regs + HANTRO_DEC_EXT_FIRST_REG, + dec_regs[id] + HANTRO_DEC_EXT_FIRST_REG, + HANTRO_DEC_EXT_REGS * 4); +#endif + if (ret) { + PDEBUG("copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + } else { + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_G2_DEC_REGS * 4)) + return -EFAULT; + + /* read all registers from hardware */ + for(i = 0; i <= HANTRO_G2_DEC_LAST_REG; i++) + dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4); + + /* put registers to user space*/ + ret = copy_to_user(core->regs, dec_regs[id], HANTRO_G2_DEC_REGS*4); + if (ret) { + PDEBUG("copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + } +#endif + return 0; +} + +static int CheckDecIrq(hantrodec_t *dev, int id) { + unsigned long flags; + int rdy = 0; + + const u32 irq_mask = (1 << id); + + spin_lock_irqsave(&owner_lock, flags); + + if(dec_irq & irq_mask) { + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + rdy = 1; + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return rdy; +} + +long WaitDecReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + u32 id = core->id; + long ret; + + PDEBUG("Hantrodec wait_event_interruptible DEC[%d]\n", id); + ret = wait_event_interruptible(dec_wait_queue, CheckDecIrq(dev, id)); + if(ret) { + PDEBUG("Hantrodec DEC[%d] wait_event_interruptible interrupted\n", id); + return -ERESTARTSYS; + } + /* xuanji: simplely imp to get core for single core, exists issue */ + CheckDecIrq(dev, id); + + atomic_inc(&irq_tx); + + /* refresh registers */ + return DecRefreshRegs(dev, core); +} + +long PPFlushRegs(hantrodec_t *dev, struct core_desc *core) { + long ret = 0; + u32 id = core->id; + u32 i; + + /* copy original dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id] + HANTRO_PP_ORG_FIRST_REG, + core->regs + HANTRO_PP_ORG_FIRST_REG, + HANTRO_PP_ORG_REGS*4); + if(sizeof(addr_t) == 8) { + /* copy extended dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id] + HANTRO_PP_EXT_FIRST_REG, + core->regs + HANTRO_PP_EXT_FIRST_REG, + HANTRO_PP_EXT_REGS*4); + } + if (ret) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + /* write all regs but the status reg[1] to hardware */ + /* both original and extended regs need to be written */ + for(i = HANTRO_PP_ORG_FIRST_REG + 1; i <= HANTRO_PP_ORG_LAST_REG; i++) + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + if(sizeof(addr_t) == 8) { + for(i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + } + /* write the stat reg, which may start the PP */ + iowrite32(dec_regs[id][HANTRO_PP_ORG_FIRST_REG], + (void*)(dev->hwregs[id] + HANTRO_PP_ORG_FIRST_REG * 4)); + + return 0; +} + +long PPRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + long i, ret; + u32 id = core->id; + if(sizeof(addr_t) == 8) { + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_PP_TOTAL_REGS * 4)) + return -EFAULT; + } else { + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_PP_ORG_REGS * 4)) + return -EFAULT; + } + + /* read all registers from hardware */ + /* both original and extended regs need to be read */ + for(i = HANTRO_PP_ORG_FIRST_REG; i <= HANTRO_PP_ORG_LAST_REG; i++) + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + if(sizeof(addr_t) == 8) { + for(i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + } + /* put registers to user space*/ + /* put original registers to user space*/ + ret = copy_to_user(core->regs + HANTRO_PP_ORG_FIRST_REG, + dec_regs[id] + HANTRO_PP_ORG_FIRST_REG, + HANTRO_PP_ORG_REGS*4); + if(sizeof(addr_t) == 8) { + /* put extended registers to user space*/ + ret = copy_to_user(core->regs + HANTRO_PP_EXT_FIRST_REG, + dec_regs[id] + HANTRO_PP_EXT_FIRST_REG, + HANTRO_PP_EXT_REGS * 4); + } + if (ret) { + PDEBUG("Hantrodec copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + + return 0; +} + +static int CheckPPIrq(hantrodec_t *dev, int id) { + unsigned long flags; + int rdy = 0; + + const u32 irq_mask = (1 << id); + + spin_lock_irqsave(&owner_lock, flags); + + if(pp_irq & irq_mask) { + /* reset the wait condition(s) */ + pp_irq &= ~irq_mask; + rdy = 1; + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return rdy; +} + +long WaitPPReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + u32 id = core->id; + + PDEBUG("Hantrodec wait_event_interruptible PP[%d]\n", id); + + if(wait_event_interruptible(pp_wait_queue, CheckPPIrq(dev, id))) { + PDEBUG("Hantrodec PP[%d] wait_event_interruptible interrupted\n", id); + return -ERESTARTSYS; + } + /* xuanji: simplely imp to get core for single core, exists issue */ + CheckPPIrq(dev, id); + + atomic_inc(&irq_tx); + + /* refresh registers */ + return PPRefreshRegs(dev, core); +} + +static int CheckCoreIrq(hantrodec_t *dev, const int filp, int *id) { + unsigned long flags; + int rdy = 0, n = 0; + + do { + u32 irq_mask = (1 << n); + + spin_lock_irqsave(&owner_lock, flags); + + if(dec_irq & irq_mask) { + if (dec_owner[n] == filp) { + /* we have an IRQ for our client */ + + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + + /* signal ready core no. for our client */ + *id = n; + + rdy = 1; + + spin_unlock_irqrestore(&owner_lock, flags); + break; + } else if(dec_owner[n] == 0) { + /* zombie IRQ */ + printk(KERN_INFO "IRQ on core[%d], but no owner!!!\n", n); + + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + n++; /* next core */ + } while(n < dev->cores); + + return rdy; +} + +long WaitCoreReady(hantrodec_t *dev, const int filp, int *id) { + PDEBUG("Hantrodec wait_event_interruptible CORE\n"); + + if(wait_event_interruptible(dec_wait_queue, CheckCoreIrq(dev, filp, id))) { + PDEBUG("Hantrodec CORE wait_event_interruptible interrupted\n"); + return -ERESTARTSYS; + } + /* xuanji: simplely imp to get core for single core, exists issue */ + CheckCoreIrq(dev, filp, id); + + atomic_inc(&irq_tx); + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_ioctl + Description : communication method to/from the user space + + Return type : long +------------------------------------------------------------------------------*/ + +long hantrodec_ioctl(int filp, unsigned int cmd, void *arg) { + int err = 0; + long tmp; +#if 0 +#ifdef CLK_CFG + unsigned long flags; +#endif +#endif + +#ifdef HW_PERFORMANCE + struct timeval *end_time_arg; +#endif + + PDEBUG("Hantrodec ioctl cmd 0x%08x\n", cmd); + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != HANTRODEC_IOC_MAGIC && + //_IOC_TYPE(cmd) != HANTRO_IOC_MMU && + _IOC_TYPE(cmd) != HANTRO_VCMD_IOC_MAGIC) + return -ENOTTY; + if ((_IOC_TYPE(cmd) == HANTRODEC_IOC_MAGIC && + _IOC_NR(cmd) > HANTRODEC_IOC_MAXNR) || + //(_IOC_TYPE(cmd) == HANTRO_IOC_MMU && + //_IOC_NR(cmd) > HANTRO_IOC_MMU_MAXNR) || + (_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_VCMD_IOC_MAXNR)) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); + + if (err) + return -EFAULT; + +#if 0 +#ifdef CLK_CFG + spin_lock_irqsave(&clk_lock, flags); + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)&&(is_clk_on==0)) { + printk("Hantrodec turn on clock by user\n"); + if (clk_enable(clk_cfg)) { + spin_unlock_irqrestore(&clk_lock, flags); + return -EFAULT; + } else + is_clk_on=1; + } + spin_unlock_irqrestore(&clk_lock, flags); + mod_timer(&timer, jiffies + 10*HZ); /*the interval is 10s*/ +#endif +#endif + + switch (cmd) { + case HANTRODEC_IOC_CLI: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + disable_irq(hantrodec_data.irq[id]); + break; + } + case HANTRODEC_IOC_STI: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + enable_irq(hantrodec_data.irq[id]); + break; + } + case HANTRODEC_IOCGHWOFFSET: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + + __put_user(multicorebase_actual[id], (unsigned long *) arg); + break; + } + case HANTRODEC_IOCGHWIOSIZE: { + __u32 id; + __u32 io_size; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + io_size = hantrodec_data.iosize[id]; + __put_user(io_size, (u32 *) arg); + + return 0; + } + case HANTRODEC_IOC_MC_OFFSETS: { + tmp = copy_to_user((unsigned long *) arg, multicorebase_actual, sizeof(multicorebase_actual)); + if (err) { + PDEBUG("Hantrodec copy_to_user failed, returned %li\n", tmp); + return -EFAULT; + } + break; + } + case HANTRODEC_IOC_MC_CORES: + __put_user(hantrodec_data.cores, (unsigned int *) arg); + PDEBUG("Hantrodec hantrodec_data.cores=%d\n", hantrodec_data.cores); + break; + case HANTRODEC_IOCS_DEC_PUSH_REG: { + struct core_desc core; + uint32_t curr_int_status = 0; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + DecFlushRegs(&hantrodec_data, &core); + curr_int_status = ReadInterruptStatus(); + break; + } + case HANTRODEC_IOCS_DEC_WRITE_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + DecWriteRegs(&hantrodec_data, &core); + break; + } + case HANTRODEC_IOCS_PP_PUSH_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + PPFlushRegs(&hantrodec_data, &core); + break; + } + case HANTRODEC_IOCS_DEC_PULL_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_DEC_READ_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecReadRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_PP_PULL_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return PPRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCH_DEC_RESERVE: { + u32 format = 0; + __get_user(format, (unsigned long *)arg); + PDEBUG("Hantrodec Reserve DEC core, format = %li\n", format); + return ReserveDecoder(&hantrodec_data, filp, format); + } + case HANTRODEC_IOCT_DEC_RELEASE: { + u32 core = *(unsigned long *)arg; + if(core >= hantrodec_data.cores || dec_owner[core] != filp) { + PDEBUG("Hantrodec bogus DEC release, core = %li\n", core); + return -EFAULT; + } + + PDEBUG("Hantrodec Release DEC, core = %li\n", core); + + ReleaseDecoder(&hantrodec_data, core); + + break; + } + case HANTRODEC_IOCQ_PP_RESERVE: + return ReservePostProcessor(&hantrodec_data, filp); + case HANTRODEC_IOCT_PP_RELEASE: { + u32 core = 0; + __get_user(core, (unsigned long *)arg); + if(core != 0 || pp_owner[core] != filp) { + PDEBUG("Hantrodec bogus PP release %li\n", core); + return -EFAULT; + } + + ReleasePostProcessor(&hantrodec_data, core); + + break; + } + case HANTRODEC_IOCX_DEC_WAIT: { + struct core_desc core; + + /* get registers from user space */ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return WaitDecReadyAndRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCX_PP_WAIT: { + struct core_desc core; + + /* get registers from user space */ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("Hantrodec copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return WaitPPReadyAndRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCG_CORE_WAIT: { + int id; + uint32_t curr_int_status = 0; + curr_int_status = ReadInterruptStatus(); + tmp = WaitCoreReady(&hantrodec_data, filp, &id); + __put_user(id, (int *) arg); + return tmp; + } + case HANTRODEC_IOX_ASIC_ID: { + u32 id; + __get_user(id, (u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + id = ioread32((void*)(hantrodec_data.hwregs[id])); + __put_user(id, (u32 *) arg); + return 0; + } + case HANTRODEC_IOCG_CORE_ID: { + u32 format = 0; + __get_user(format, (unsigned long *)arg); + + PDEBUG("Hantrodec Get DEC Core_id, format = %li\n", format); + return GetDecCoreID(&hantrodec_data, filp, format); + } + case HANTRODEC_IOX_ASIC_BUILD_ID: { + u32 id, hw_id; + __get_user(id, (u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + hw_id = ioread32((void*)(hantrodec_data.hwregs[id])); + if (IS_G1(hw_id >> 16) || IS_G2(hw_id >> 16)) + __put_user(hw_id, (u32 *) arg); + else { + hw_id = ioread32((void*)(hantrodec_data.hwregs[id] + HANTRODEC_HW_BUILD_ID_OFF)); + __put_user(hw_id, (u32 *) arg); + } + return 0; + } + case HANTRODEC_DEBUG_STATUS: { + printk(KERN_INFO "hantrodec: dec_irq = 0x%08x \n", dec_irq); + printk(KERN_INFO "hantrodec: pp_irq = 0x%08x \n", pp_irq); + + printk(KERN_INFO "hantrodec: IRQs received/sent2user = %d / %d \n", + atomic_read(&irq_rx), atomic_read(&irq_tx)); + + for (tmp = 0; tmp < hantrodec_data.cores; tmp++) { + printk(KERN_INFO "hantrodec: dec_core[%li] %s\n", + tmp, dec_owner[tmp] == NULL ? "FREE" : "RESERVED"); + printk(KERN_INFO "hantrodec: pp_core[%li] %s\n", + tmp, pp_owner[tmp] == NULL ? "FREE" : "RESERVED"); + } + return 0; + } + case HANTRODEC_IOX_SUBSYS: { + struct subsys_desc subsys = {0}; + /* TODO(min): check all the subsys */ + if (vcmd) { + subsys.subsys_vcmd_num = 1; + subsys.subsys_num = subsys.subsys_vcmd_num; + } else { + subsys.subsys_num = hantrodec_data.cores; + subsys.subsys_vcmd_num = 0; + } + copy_to_user((u32 *) arg, &subsys, sizeof(struct subsys_desc)); + return 0; + } + case HANTRODEC_IOCX_POLL: { + hantrodec_isr(&hantrodec_data); + return 0; + } + default: + if (_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC) { + return (hantrovcmd_ioctl(filp, cmd, arg)); + } + return -ENOTTY; + } + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_open + Description : open method + + Return type : int +------------------------------------------------------------------------------*/ + +int hantrodec_open(int *inode, int filp) { + PDEBUG("Hantrodec dev opened\n"); + + if (vcmd) + hantrovcmd_open(inode, filp); + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_release + Description : Release driver + + Return type : int +------------------------------------------------------------------------------*/ + +int hantrodec_release(int *inode, int filp) { + int n; + hantrodec_t *dev = &hantrodec_data; + + PDEBUG("Hantrodec closing ...\n"); + + if (vcmd) { + hantrovcmd_release(inode, filp); + return 0; + } + + for(n = 0; n < dev->cores; n++) { + if(dec_owner[n] == filp) { + PDEBUG("Hantrodec releasing dec core %i lock\n", n); + ReleaseDecoder(dev, n); + } + } + + for(n = 0; n < 1; n++) { + if(pp_owner[n] == filp) { + PDEBUG("Hantrodec releasing pp core %i lock\n", n); + ReleasePostProcessor(dev, n); + } + } + + PDEBUG("Hantrodec closed\n"); + return 0; +} + +#if 0 +#ifdef CLK_CFG +void hantrodec_disable_clk(unsigned long value) { + unsigned long flags; + /*entering this function means decoder is idle over expiry.So disable clk*/ + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)) { + spin_lock_irqsave(&clk_lock, flags); + if (is_clk_on==1) { + clk_disable(clk_cfg); + is_clk_on = 0; + printk("turned off hantrodec clk\n"); + } + spin_unlock_irqrestore(&clk_lock, flags); + } +} +#endif +#endif + +/* VFS methods */ +/* +static struct file_operations hantrodec_fops = { + .owner = THIS_MODULE, + .open = hantrodec_open, + .release = hantrodec_release, + .unlocked_ioctl = hantrodec_ioctl, + .fasync = NULL +}; +*/ + +/*------------------------------------------------------------------------------ + Function name : hantrodec_init + Description : Initialize the driver + + Return type : int +------------------------------------------------------------------------------*/ +int __init hantrodec_init(void) { + int result, i; + + PDEBUG("Hantrodec module init\n"); + + CheckSubsysCoreArray(vpu_subsys, &vcmd); + + if (vcmd) { + result = hantrovcmd_init(); + if (result) return result; + + return 0; + } + + printk(KERN_INFO "hantrodec: dec/pp kernel module. \n"); + + /* If base_port is set, use that for single core legacy mode */ + if(base_port != 0) { + multicorebase[0] = base_port; + elements = 1; + printk(KERN_INFO "hantrodec: Init single core at 0x%08lx IRQ=%i\n", + multicorebase[0], irq[0]); + } else { + printk(KERN_INFO "hantrodec: Init multi core[0] at 0x%16lx\n" + " core[1] at 0x%16lx\n" + " core[2] at 0x%16lx\n" + " core[3] at 0x%16lx\n" + " IRQ_0=%i\n" + " IRQ_1=%i\n", + multicorebase[0], multicorebase[1], + multicorebase[2], multicorebase[3], + irq[0],irq[1]); + } + + hantrodec_data.cores = 0; + + hantrodec_data.iosize[0] = DEC_IO_SIZE_0; + hantrodec_data.irq[0] = irq[0]; + hantrodec_data.iosize[1] = DEC_IO_SIZE_1; + hantrodec_data.irq[1] = irq[1]; + + for(i=0; i< HXDEC_MAX_CORES; i++) { + hantrodec_data.hwregs[i] = 0; + /* If user gave less core bases that we have by default, + * invalidate default bases + */ + if(elements && i>=elements) { + multicorebase[i] = -1; + } + } + + //hantrodec_data.async_queue_dec = NULL; + //hantrodec_data.async_queue_pp = NULL; + + result = register_chrdev(hantrodec_major, "hantrodec", &hantrodec_fops); + if(result < 0) { + printk(KERN_INFO "hantrodec: unable to get major %d\n", hantrodec_major); + goto err; + } else if(result != 0) { /* this is for dynamic major */ + hantrodec_major = result; + } + +#if 0 +#ifdef CLK_CFG + /* first get clk instance pointer */ + clk_cfg = clk_get(NULL, CLK_ID); + if (!clk_cfg||IS_ERR(clk_cfg)) { + printk("get handrodec clk failed!\n"); + goto err; + } + + /* prepare and enable clk */ + if(clk_prepare_enable(clk_cfg)) { + printk("try to enable handrodec clk failed!\n"); + goto err; + } + is_clk_on = 1; + + /*init a timer to disable clk*/ + init_timer(&timer); + timer.function = &hantrodec_disable_clk; + timer.expires = jiffies + 100*HZ; //the expires time is 100s + add_timer(&timer); +#endif +#endif + + result = ReserveIO(); + if(result < 0) { + goto err; + } + + memset(dec_owner, 0, sizeof(dec_owner)); + memset(pp_owner, 0, sizeof(pp_owner)); + + sema_init(&dec_core_sem, hantrodec_data.cores); + sema_init(&pp_core_sem, 1); + + sem_init(&dec_wait_queue, 0, 0); + sem_init(&pp_wait_queue, 0, 0); + sem_init(&hw_queue, 0, hantrodec_data.cores); + + /* read configuration fo all cores */ + ReadCoreConfig(&hantrodec_data); + + /* reset hardware */ + ResetAsic(&hantrodec_data); + + /* register irq for each core */ + if(irq[0] > 0) { + result = request_irq(irq[0], hantrodec_isr, IRQF_SHARED, "hantrodec", (void *) &hantrodec_data); + + if(result != 0) { + if(result == -EINVAL) { + printk(KERN_ERR "hantrodec: Bad irq number or handler\n"); + } else if(result == -EBUSY) { + printk(KERN_ERR "hantrodec: IRQ <%d> busy, change your config\n", + hantrodec_data.irq[0]); + } + + ReleaseIO(); + goto err; + } + } else { + printk(KERN_INFO "hantrodec: IRQ not in use!\n"); + } + + if(irq[1] > 0) { + result = request_irq(irq[1], hantrodec_isr, +//#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) +// SA_INTERRUPT | SA_SHIRQ, +//#else + IRQF_SHARED, +//#endif + "hantrodec", (void *) &hantrodec_data); + + if(result != 0) { + if(result == -EINVAL) { + printk(KERN_ERR "hantrodec: Bad irq number or handler\n"); + } else if(result == -EBUSY) { + printk(KERN_ERR "hantrodec: IRQ <%d> busy, change your config\n", + hantrodec_data.irq[1]); + } + + ReleaseIO(); + goto err; + } + } else { + printk(KERN_INFO "hantrodec: IRQ not in use!\n"); + } + + printk(KERN_INFO "hantrodec: module inserted. Major = %d\n", hantrodec_major); + + return 0; + +err: + ReleaseIO(); + printk(KERN_INFO "hantrodec: module not inserted\n"); + unregister_chrdev(hantrodec_major, "hantrodec"); + return result; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_cleanup + Description : clean up + + Return type : int +------------------------------------------------------------------------------*/ +void __exit hantrodec_cleanup(void) { + hantrodec_t *dev = &hantrodec_data; + int n = 0; + /* reset hardware */ + ResetAsic(dev); + + /* free the IRQ */ + for (n = 0; n < dev->cores; n++) { + if(dev->irq[n] != -1) { + free_irq(dev->irq[n], (void *) dev); + } + } + + ReleaseIO(); + +#if 0 +#ifdef CLK_CFG + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)) { + clk_disable_unprepare(clk_cfg); + is_clk_on = 0; + printk("turned off hantrodec clk\n"); + } + + /*delete timer*/ + del_timer(&timer); +#endif +#endif + unregister_chrdev(hantrodec_major, "hantrodec"); + + printk(KERN_INFO "hantrodec: module removed\n"); + return; +} +/*------------------------------------------------------------------------------ + Function name : CheckHwId + Return type : int +------------------------------------------------------------------------------*/ +static int CheckHwId(hantrodec_t * dev) { + long int hwid; + int i; + size_t num_hw = sizeof(DecHwId) / sizeof(*DecHwId); + + int found = 0; + + for (i = 0; i < dev->cores; i++) { + if (dev->hwregs[i] != NULL ) { + hwid = readl(dev->hwregs[i]); + printk(KERN_INFO "hantrodec: core %d HW ID=0x%16lx\n", i, hwid); + hwid = (hwid >> 16) & 0xFFFF; /* product version only */ + + while (num_hw--) { + if (hwid == DecHwId[num_hw]) { + printk(KERN_INFO "hantrodec: Supported HW found at 0x%16lx\n", + multicorebase_actual[i]); + found++; + dev->hw_id[i] = hwid; + break; + } + } + if (!found) { + printk(KERN_INFO "hantrodec: Unknown HW found at 0x%16lx\n", + multicorebase_actual[i]); + return 0; + } + found = 0; + num_hw = sizeof(DecHwId) / sizeof(*DecHwId); + } + } + + return 1; +} + +/*------------------------------------------------------------------------------ + Function name : ReserveIO + Description : IO reserve + + Return type : int +------------------------------------------------------------------------------*/ +static int ReserveIO(void) { + int i = 0; + long int hwid = 0; + u32 reg = 0; + + memcpy(multicorebase_actual, multicorebase, HXDEC_MAX_CORES * sizeof(unsigned long)); + memcpy((unsigned int*)(hantrodec_data.iosize), iosize, HXDEC_MAX_CORES * sizeof(unsigned int)); + memcpy((unsigned int*)(hantrodec_data.irq), irq, HXDEC_MAX_CORES * sizeof(int)); + + for (i = 0; i < HXDEC_MAX_CORES; i++) { + if (multicorebase_actual[i] != -1) { + if (!request_mem_region(multicorebase_actual[i], hantrodec_data.iosize[i], + "hantrodec0")) { + printk(KERN_INFO "hantrodec: failed to reserve HW regs\n"); + return -EBUSY; + } + + hantrodec_data.hwregs[i] = (volatile u8 *) ioremap_nocache(multicorebase_actual[i], + hantrodec_data.iosize[i]); + + if (hantrodec_data.hwregs[i] == NULL ) { + printk(KERN_INFO "hantrodec: failed to ioremap HW regs\n"); + ReleaseIO(); + return -EBUSY; + } + hantrodec_data.cores++; + config.its_main_core_id[i] = -1; + config.its_aux_core_id[i] = -1; + + hwid = ((readl(hantrodec_data.hwregs[i])) >> 16) & 0xFFFF; /* product version only */ + + if (IS_VC8000D(hwid)) { + reg = readl(hantrodec_data.hwregs[i] + HANTRODEC_SYNTH_CFG_2_OFF); + if (((reg >> DWL_H264_PIPELINE_E) & 0x01U) || ((reg >> DWL_JPEG_PIPELINE_E) & 0x01U)) { + i++; + config.its_aux_core_id[i-1] = i; + config.its_main_core_id[i] = i-1; + config.its_aux_core_id[i] = -1; + multicorebase_actual[i] = multicorebase_actual[i-1] + 0x800; + hantrodec_data.iosize[i] = hantrodec_data.iosize[i-1]; + memcpy(multicorebase_actual+i+1, multicorebase+i, + (HXDEC_MAX_CORES - i - 1) * sizeof(unsigned long)); + memcpy((unsigned int*)hantrodec_data.iosize+i+1, iosize+i, + (HXDEC_MAX_CORES - i - 1) * sizeof(unsigned int)); + if (!request_mem_region(multicorebase_actual[i], hantrodec_data.iosize[i], + "hantrodec0")) { + printk(KERN_INFO "hantrodec: failed to reserve HW regs\n"); + return -EBUSY; + } + + hantrodec_data.hwregs[i] = (volatile u8 *) ioremap_nocache(multicorebase_actual[i], + hantrodec_data.iosize[i]); + + if (hantrodec_data.hwregs[i] == NULL ) { + printk(KERN_INFO "hantrodec: failed to ioremap HW regs\n"); + ReleaseIO(); + return -EBUSY; + } + hantrodec_data.cores++; + } + } + } + } + + /* check for correct HW */ + if (!CheckHwId(&hantrodec_data)) { + ReleaseIO(); + return -EBUSY; + } + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : releaseIO + Description : release + + Return type : void +------------------------------------------------------------------------------*/ + +static void ReleaseIO(void) { + int i = 0; + for (i = 0; i < hantrodec_data.cores; i++) { + if (hantrodec_data.hwregs[i]) + iounmap((void *) hantrodec_data.hwregs[i]); + release_mem_region(multicorebase_actual[i], hantrodec_data.iosize[i]); + } +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_isr + Description : interrupt handler + + Return type : irqreturn_t +------------------------------------------------------------------------------*/ +static irqreturn_t hantrodec_isr(void *args) { + unsigned long flags = 0; + unsigned int handled = 0; + int i = 0; + volatile u8 *hwregs = NULL; + + hantrodec_t *dev = (hantrodec_t *) args; + u32 irq_status_dec = 0; + + spin_lock_irqsave(&owner_lock, flags); + IntClearIRQStatus(irq[0], 0); + + for(i=0; icores; i++) { + volatile u8 *hwregs = dev->hwregs[i]; + + /* interrupt status register read */ + irq_status_dec = ioread32((void*)(hwregs + HANTRODEC_IRQ_STAT_DEC_OFF)); + + if(irq_status_dec & HANTRODEC_DEC_IRQ) { + /* clear dec IRQ */ + irq_status_dec &= (~HANTRODEC_DEC_IRQ); + iowrite32(irq_status_dec, (void*)(hwregs + HANTRODEC_IRQ_STAT_DEC_OFF)); + + PDEBUG("Hantrodec decoder IRQ received! core %d\n", i); + + atomic_inc(&irq_rx); + + dec_irq |= (1 << i); + + wake_up_interruptible_all(&dec_wait_queue); + handled++; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + if(!handled) { + PDEBUG("Hantrodec IRQ received, but not hantrodec's!\n"); + } + + (void)flags; + return IRQ_RETVAL(handled); +} + +/*------------------------------------------------------------------------------ + Function name : ResetAsic + Description : reset asic + + Return type : +------------------------------------------------------------------------------*/ +void ResetAsic(hantrodec_t * dev) { + int i, j; + u32 status; + + for (j = 0; j < dev->cores; j++) { + status = ioread32((void *)(dev->hwregs[j] + HANTRODEC_IRQ_STAT_DEC_OFF)); + + if( status & HANTRODEC_DEC_E) { + /* abort with IRQ disabled */ + status = HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void *)(dev->hwregs[j] + HANTRODEC_IRQ_STAT_DEC_OFF)); + } + + if (IS_G1(dev->hw_id[j])) + /* reset PP */ + iowrite32(0, (void *)(dev->hwregs[j] + HANTRO_IRQ_STAT_PP_OFF)); + + for (i = 4; i < dev->iosize[j]; i += 4) { + iowrite32(0, (void *)(dev->hwregs[j] + i)); + } + } +} + +/*------------------------------------------------------------------------------ + Function name : dump_regs + Description : Dump registers + + Return type : +------------------------------------------------------------------------------*/ +#ifdef HANTRODEC_DEBUG +void dump_regs(hantrodec_t *dev) { + int i,c; + + PDEBUG("Hantrodec Reg Dump Start\n"); + for(c = 0; c < dev->cores; c++) { + for(i = 0; i < dev->iosize[c]; i += 4*4) { + PDEBUG("\toffset %04X: %08X %08X %08X %08X\n", i, + ioread32(dev->hwregs[c] + i), + ioread32(dev->hwregs[c] + i + 4), + ioread32(dev->hwregs[c] + i + 16), + ioread32(dev->hwregs[c] + i + 24)); + } + } + PDEBUG("Hantrodec Reg Dump End\n"); +} +#endif + +av_unused int RegisterIRQ(i32 irq_offset, IRQHandler isr_handler, i32 flag, const char* name, void* data) { +//_xtos_set_interrupt_handle + u32 irq_mask = 1 << irq_offset; //irq_offset -> Irq[0] is 3 + u32 enabled_int = 0; + if ((xthal_get_intenable() & irq_mask) == 0) + { + /* Clear MCPU DB2 interrupt before enable */ + xthal_set_intclear(irq_mask); + /* Assign Interrupt handler */ + xt_set_interrupt_handler(irq_offset, (xt_handler) isr_handler, data); + + *((volatile uint32_t *)0x0280004c) = 0x1; // clear current irq for slice 0 + *((volatile uint32_t *)0x02800050) = 0x1; // enable dec irq for slice 0 + + xt_ints_on(irq_mask); + } + enabled_int = xthal_get_intenable(); + printf("INTs are enabled: 0x%x\n", enabled_int); + return 0; +} + +av_unused void IntEnableIRQ(u32 irq_offset) { + //_xtos_interrupt_enable + u32 irq_mask = 1 << irq_offset; + u32 enabled_int = 0; + xt_ints_on(irq_mask); + + enabled_int = xthal_get_intenable(); + printf("INTs are enabled 2: 0x%x\n", enabled_int); +} + +av_unused void IntDisableIRQ(u32 irq_offset) { + //_xtos_interrupt_disable + u32 irq_mask = 1 << irq_offset; + u32 enabled_int = 0; + xt_ints_off(irq_mask); + enabled_int = xthal_get_intenable(); + printf("INTs are disabled 3: 0x%x\n", enabled_int); +} + +av_unused void IntClearIRQStatus(u32 irq_offset, u32 type) { + //xthal_set_intclear + *((volatile uint32_t *)0x0280004c) = 0x1; // clear current irq for slice 0 + u32 irq_mask = 1 << irq_offset; + xthal_set_intclear(irq_mask); +} + +static inline uint32_t ReadInterruptStatus(void) { + uint32_t interrupt; + __asm__ __volatile__("rsr %0, interrupt": "=a"(interrupt)); + return interrupt; +} +//For debug +av_unused u32 IntGetIRQStatus(u32 irq_offset) { + uint32_t ret_val; + //ReadInterruptStatus + ret_val = ReadInterruptStatus(); + //mask other interrupts ? + ret_val &= (1 << irq_offset); + // return 1 if requested interrupt is set + if (ret_val) + ret_val = 1; + return 0; +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_vcmd_freertos.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_vcmd_freertos.c new file mode 100644 index 000000000..5c399e7dc --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/hantro_vcmd_freertos.c @@ -0,0 +1,3843 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/*------------------------------------------------------------------------------ +-- Abstract : vc8000 Vcmd device driver (kernel module) +------------------------------------------------------------------------------*/ +/* our own stuff */ +#include "vcmdswhwregisters.h" +#include "bidirect_list.h" +#include "hantrovcmd.h" +#include "subsys.h" +#include "io_tools.h" +#include "osal.h" +#include "memalloc_freertos.h" + +//Netint xtensa cpu and FreeRTOS OS +#include "xtensa_api.h" +//#include "../src/libxmp/xmp-library.h" //For atomic operations +#include //interrupt for xtensa + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef HANTRO_VCMD_DRIVER_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "vc8000_vcmd: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/*----------------------------------------------------------------------------------------- +************************CPU Xtensa OS FreeRTOS PORTING LAYER******************************* +------------------------------------------------------------------------------------------*/ +static u32 g_vc8000_int_enable_mask = 0; +//VCMD for netint +#define SYS_REG_INT_TOP_BASE (0x02800000) +//ENCODER +#if 0 +#define CPU_INT_IRQ 8 /* All Encoder Modules' interrupt will be connected to CPU IRQ 8 */ +#define SYS_INT_MASK (0x220) +#define SYS_REG_INT_VAL (SYS_REG_INT_TOP_BASE + 0x3c) +#define SYS_REG_INT_STAT (SYS_REG_INT_TOP_BASE + 0x40) +#define SYS_REG_INT_EN (SYS_REG_INT_TOP_BASE + 0x44) +#else +//DECODER +#define CPU_INT_IRQ 9 +#define SYS_INT_MASK (0x001) +#define SYS_REG_INT_VAL (SYS_REG_INT_TOP_BASE + 0x48) +#define SYS_REG_INT_STAT (SYS_REG_INT_TOP_BASE + 0x4c) +#define SYS_REG_INT_EN (SYS_REG_INT_TOP_BASE + 0x50) +#endif +/*------------------------------------------------------------------------ +*****************************VCMD CONFIGURATION BY CUSTOMER******************************** +-------------------------------------------------------------------------*/ +//video encoder vcmd configuration +#define VCMD_ENC_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_0 -1 +#define VCMD_ENC_MODULE_TYPE_0 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_ENC_IO_ADDR_1 (0x02300000 + 0x40000) /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_1 5 +#define VCMD_ENC_MODULE_TYPE_1 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_1 0x1000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_1 0X2000 /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_ENC_IO_ADDR_2 0x92000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_2 -1 +#define VCMD_ENC_MODULE_TYPE_2 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_ENC_IO_ADDR_3 0x93000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_3 -1 +#define VCMD_ENC_MODULE_TYPE_3 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_3 0XFFFF + +//video encoder cutree/IM vcmd configuration +#define VCMD_IM_IO_ADDR_0 0xa00000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_0 -1 +#define VCMD_IM_MODULE_TYPE_0 1 +#define VCMD_IM_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_IM_IO_ADDR_1 (0x02300000 + 0x44000) /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_1 9 +#define VCMD_IM_MODULE_TYPE_1 1 +#define VCMD_IM_MODULE_MAIN_ADDR_1 0x1000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_IM_IO_ADDR_2 0xa2000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_2 -1 +#define VCMD_IM_MODULE_TYPE_2 1 +#define VCMD_IM_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_IM_IO_ADDR_3 0xa3000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_3 -1 +#define VCMD_IM_MODULE_TYPE_3 1 +#define VCMD_IM_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_3 0XFFFF + +//video decoder vcmd configuration +#define VCMD_DEC_IO_ADDR_0 0x02310000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_0 0 +#define VCMD_DEC_MODULE_TYPE_0 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_0 0x2000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_0 0X6000 /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_0 0X4000 +#define VCMD_DEC_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_DEC_IO_ADDR_1 0xb1000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_1 -1 +#define VCMD_DEC_MODULE_TYPE_1 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_DEC_IO_ADDR_2 0xb2000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_2 -1 +#define VCMD_DEC_MODULE_TYPE_2 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_DEC_IO_ADDR_3 0xb3000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_3 -1 +#define VCMD_DEC_MODULE_TYPE_3 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_3 0XFFFF + +//JPEG encoder vcmd configuration +#define VCMD_JPEGE_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_0 -1 +#define VCMD_JPEGE_MODULE_TYPE_0 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_1 0xC1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_1 -1 +#define VCMD_JPEGE_MODULE_TYPE_1 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_2 0xC2000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_2 -1 +#define VCMD_JPEGE_MODULE_TYPE_2 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_3 0xC3000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_3 -1 +#define VCMD_JPEGE_MODULE_TYPE_3 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_3 0XFFFF + +//JPEG decoder vcmd configuration +#define VCMD_JPEGD_IO_ADDR_0 0xD0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_0 -1 +#define VCMD_JPEGD_MODULE_TYPE_0 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_1 0xD1000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_1 -1 +#define VCMD_JPEGD_MODULE_TYPE_1 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_2 0xD2000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_2 -1 +#define VCMD_JPEGD_MODULE_TYPE_2 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_3 0xD3000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_3 -1 +#define VCMD_JPEGD_MODULE_TYPE_3 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_3 0XFFFF + +#define NETINT +//#define MAGVII + +/*for all vcmds, the core info should be listed here for subsequent use*/ +struct vcmd_config vcmd_core_array[] = { +//encoder configuration +#if 0 + {VCMD_ENC_IO_ADDR_0, + VCMD_ENC_IO_SIZE_0, + VCMD_ENC_INT_PIN_0, + VCMD_ENC_MODULE_TYPE_0, + VCMD_ENC_MODULE_MAIN_ADDR_0, + VCMD_ENC_MODULE_DEC400_ADDR_0, + VCMD_ENC_MODULE_L2CACHE_ADDR_0, + VCMD_ENC_MODULE_MMU_ADDR_0}, + + {VCMD_ENC_IO_ADDR_1, + VCMD_ENC_IO_SIZE_1, + VCMD_ENC_INT_PIN_1, + VCMD_ENC_MODULE_TYPE_1, + VCMD_ENC_MODULE_MAIN_ADDR_1, + VCMD_ENC_MODULE_DEC400_ADDR_1, + VCMD_ENC_MODULE_L2CACHE_ADDR_1, + VCMD_ENC_MODULE_MMU_ADDR_1}, + + {VCMD_ENC_IO_ADDR_2, + VCMD_ENC_IO_SIZE_2, + VCMD_ENC_INT_PIN_2, + VCMD_ENC_MODULE_TYPE_2, + VCMD_ENC_MODULE_MAIN_ADDR_2, + VCMD_ENC_MODULE_DEC400_ADDR_2, + VCMD_ENC_MODULE_L2CACHE_ADDR_2, + VCMD_ENC_MODULE_MMU_ADDR_2}, + + {VCMD_ENC_IO_ADDR_3, + VCMD_ENC_IO_SIZE_3, + VCMD_ENC_INT_PIN_3, + VCMD_ENC_MODULE_TYPE_3, + VCMD_ENC_MODULE_MAIN_ADDR_3, + VCMD_ENC_MODULE_DEC400_ADDR_3, + VCMD_ENC_MODULE_L2CACHE_ADDR_3, + VCMD_ENC_MODULE_MMU_ADDR_3}, + +//cutree/IM configuration + + {VCMD_IM_IO_ADDR_0, + VCMD_IM_IO_SIZE_0, + VCMD_IM_INT_PIN_0, + VCMD_IM_MODULE_TYPE_0, + VCMD_IM_MODULE_MAIN_ADDR_0, + VCMD_IM_MODULE_DEC400_ADDR_0, + VCMD_IM_MODULE_L2CACHE_ADDR_0, + VCMD_IM_MODULE_MMU_ADDR_0}, + + {VCMD_IM_IO_ADDR_1, + VCMD_IM_IO_SIZE_1, + VCMD_IM_INT_PIN_1, + VCMD_IM_MODULE_TYPE_1, + VCMD_IM_MODULE_MAIN_ADDR_1, + VCMD_IM_MODULE_DEC400_ADDR_1, + VCMD_IM_MODULE_L2CACHE_ADDR_1, + VCMD_IM_MODULE_MMU_ADDR_1}, + + {VCMD_IM_IO_ADDR_2, + VCMD_IM_IO_SIZE_2, + VCMD_IM_INT_PIN_2, + VCMD_IM_MODULE_TYPE_2, + VCMD_IM_MODULE_MAIN_ADDR_2, + VCMD_IM_MODULE_DEC400_ADDR_2, + VCMD_IM_MODULE_L2CACHE_ADDR_2, + VCMD_IM_MODULE_MMU_ADDR_2}, + + {VCMD_IM_IO_ADDR_3, + VCMD_IM_IO_SIZE_3, + VCMD_IM_INT_PIN_3, + VCMD_IM_MODULE_TYPE_3, + VCMD_IM_MODULE_MAIN_ADDR_3, + VCMD_IM_MODULE_DEC400_ADDR_3, + VCMD_IM_MODULE_L2CACHE_ADDR_3, + VCMD_IM_MODULE_MMU_ADDR_3}, +#endif +#ifdef NETINT +//decoder configuration + {VCMD_DEC_IO_ADDR_0, + VCMD_DEC_IO_SIZE_0, + VCMD_DEC_INT_PIN_0, + VCMD_DEC_MODULE_TYPE_0, + VCMD_DEC_MODULE_MAIN_ADDR_0, + VCMD_DEC_MODULE_DEC400_ADDR_0, + VCMD_DEC_MODULE_L2CACHE_ADDR_0, + VCMD_DEC_MODULE_MMU_ADDR_0}, +#endif +#if 0 + {VCMD_DEC_IO_ADDR_1, + VCMD_DEC_IO_SIZE_1, + VCMD_DEC_INT_PIN_1, + VCMD_DEC_MODULE_TYPE_1, + VCMD_DEC_MODULE_MAIN_ADDR_1, + VCMD_DEC_MODULE_DEC400_ADDR_1, + VCMD_DEC_MODULE_L2CACHE_ADDR_1, + VCMD_DEC_MODULE_MMU_ADDR_1}, + + {VCMD_DEC_IO_ADDR_2, + VCMD_DEC_IO_SIZE_2, + VCMD_DEC_INT_PIN_2, + VCMD_DEC_MODULE_TYPE_2, + VCMD_DEC_MODULE_MAIN_ADDR_2, + VCMD_DEC_MODULE_DEC400_ADDR_2, + VCMD_DEC_MODULE_L2CACHE_ADDR_2, + VCMD_DEC_MODULE_MMU_ADDR_2}, + + {VCMD_DEC_IO_ADDR_3, + VCMD_DEC_IO_SIZE_3, + VCMD_DEC_INT_PIN_3, + VCMD_DEC_MODULE_TYPE_3, + VCMD_DEC_MODULE_MAIN_ADDR_3, + VCMD_DEC_MODULE_DEC400_ADDR_3, + VCMD_DEC_MODULE_L2CACHE_ADDR_3, + VCMD_DEC_MODULE_MMU_ADDR_3}, +#endif +#ifdef MAGVII + //JPEG encoder configuration + {VCMD_JPEGE_IO_ADDR_0, + VCMD_JPEGE_IO_SIZE_0, + VCMD_JPEGE_INT_PIN_0, + VCMD_JPEGE_MODULE_TYPE_0, + VCMD_JPEGE_MODULE_MAIN_ADDR_0, + VCMD_JPEGE_MODULE_DEC400_ADDR_0, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_0, + VCMD_JPEGE_MODULE_MMU_ADDR_0}, +#endif +#if 0 + {VCMD_JPEGE_IO_ADDR_1, + VCMD_JPEGE_IO_SIZE_1, + VCMD_JPEGE_INT_PIN_1, + VCMD_JPEGE_MODULE_TYPE_1, + VCMD_JPEGE_MODULE_MAIN_ADDR_1, + VCMD_JPEGE_MODULE_DEC400_ADDR_1, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGE_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGE_IO_ADDR_2, + VCMD_JPEGE_IO_SIZE_2, + VCMD_JPEGE_INT_PIN_2, + VCMD_JPEGE_MODULE_TYPE_2, + VCMD_JPEGE_MODULE_MAIN_ADDR_2, + VCMD_JPEGE_MODULE_DEC400_ADDR_2, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGE_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGE_IO_ADDR_3, + VCMD_JPEGE_IO_SIZE_3, + VCMD_JPEGE_INT_PIN_3, + VCMD_JPEGE_MODULE_TYPE_3, + VCMD_JPEGE_MODULE_MAIN_ADDR_3, + VCMD_JPEGE_MODULE_DEC400_ADDR_3, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGE_MODULE_MMU_ADDR_3}, +//JPEG decoder configuration + {VCMD_JPEGD_IO_ADDR_0, + VCMD_JPEGD_IO_SIZE_0, + VCMD_JPEGD_INT_PIN_0, + VCMD_JPEGD_MODULE_TYPE_0, + VCMD_JPEGD_MODULE_MAIN_ADDR_0, + VCMD_JPEGD_MODULE_DEC400_ADDR_0, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_0, + VCMD_JPEGD_MODULE_MMU_ADDR_0}, + + {VCMD_JPEGD_IO_ADDR_1, + VCMD_JPEGD_IO_SIZE_1, + VCMD_JPEGD_INT_PIN_1, + VCMD_JPEGD_MODULE_TYPE_1, + VCMD_JPEGD_MODULE_MAIN_ADDR_1, + VCMD_JPEGD_MODULE_DEC400_ADDR_1, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGD_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGD_IO_ADDR_2, + VCMD_JPEGD_IO_SIZE_2, + VCMD_JPEGD_INT_PIN_2, + VCMD_JPEGD_MODULE_TYPE_2, + VCMD_JPEGD_MODULE_MAIN_ADDR_2, + VCMD_JPEGD_MODULE_DEC400_ADDR_2, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGD_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGD_IO_ADDR_3, + VCMD_JPEGD_IO_SIZE_3, + VCMD_JPEGD_INT_PIN_3, + VCMD_JPEGD_MODULE_TYPE_3, + VCMD_JPEGD_MODULE_MAIN_ADDR_3, + VCMD_JPEGD_MODULE_DEC400_ADDR_3, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGD_MODULE_MMU_ADDR_3}, +#endif +}; +/*these size need to be modified according to hw config.*/ +#define VCMD_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_DECODER_REGISTER_SIZE (512 * 4) +#define VCMD_IM_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_DECODER_REGISTER_SIZE (512 * 4) + +#define MAX_VCMD_NUMBER (MAX_VCMD_TYPE*MAX_SAME_MODULE_TYPE_CORE_NUMBER) // + +#define HW_WORK_STATE_PEND 3 + +#define MAX_CMDBUF_INT_NUMBER 1 +#define INT_MIN_SUM_OF_IMAGE_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_CMDBUF_INT_NUMBER) + +#if PROCESS_MANAGER +#define MAX_PROCESS_CORE_NUMBER 4*8 +#define PROCESS_MAX_SUM_OF_IMAGE_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_PROCESS_CORE_NUMBER) +#endif + +#define MAX_SAME_MODULE_TYPE_CORE_NUMBER 4 + +/*******************PCIE CONFIG*************************/ +#define PCI_VENDOR_ID_HANTRO 0x10ee //0x16c3 +#define PCI_DEVICE_ID_HANTRO_PCI 0x8014 // 0x7011 +/* Base address got control register */ +#define PCI_H2_BAR 4 +/* Base address DDR register */ +#define PCI_DDR_BAR 0 +/*struct pci_dev * */ void *g_vcmd_dev = NULL; /* PCI device structure. */ +unsigned long g_vcmd_base_hdwr = 0; /* PCI base register address (Hardware address) */ +unsigned long g_vcmd_base_ddr_hw = 0; /* PCI base register address (memalloc) */ +u32 g_vcmd_base_len = 0; /* Base register address Length */ +static ptr_t /*size_t*/ base_ddr_addr = 0; /*pcie address need to substract this value then can be put to register*/ + +/********variables declaration related with race condition**********/ +#define CMDBUF_MAX_SIZE (512 * 4 * 4) +#define CMDBUF_POOL_TOTAL_SIZE (2 * 1024 * 1024) //approximately=2Mbytes +#define TOTAL_DISCRETE_CMDBUF_NUM (CMDBUF_POOL_TOTAL_SIZE / CMDBUF_MAX_SIZE) +#define CMDBUF_VCMD_REGISTER_TOTAL_SIZE 0x100000 // 1M +#define VCMD_REGISTER_SIZE (128 * 4) + +struct noncache_mem +{ + u32 *virtualAddress; + ptr_t /* dma_addr_t */ busAddress; + u32 size; + u16 cmdbuf_id; +}; + +#if PROCESS_MANAGER +struct process_manager_obj +{ + /*struct file */ int filp; + u32 total_exe_time; + spinlock_t spinlock; + /* wait_queue_head_t */ sem_t wait_queue; +}; +#endif + +struct cmdbuf_obj +{ + u32 module_type; //current CMDBUF type: input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4 + u32 priority; //current CMDBUFpriority: normal=0, high=1 + u32 executing_time; //current CMDBUFexecuting_time=encoded_image_size*(rdoLevel+1)*(rdoq+1); + u32 cmdbuf_size; //current CMDBUF size + u32 *cmdbuf_virtualAddress; //current CMDBUF start virtual address. + ptr_t /*size_t*/ cmdbuf_busAddress; //current CMDBUF start physical address. + u32 *status_virtualAddress; //current status CMDBUF start virtual address. + ptr_t /*size_t*/ status_busAddress; //current status CMDBUF start physical address. + u32 status_size; //current status CMDBUF size + u32 executing_status; //current CMDBUF executing status. + /*struct file */ int filp; //file pointer in the same process. + u16 core_id; //which vcmd core is used. + u16 cmdbuf_id; //used to manage CMDBUF in driver.It is a handle to identify cmdbuf.also is an interrupt vector.position in pool,same as status position. + u8 cmdbuf_data_loaded; //0 means sw has not copied data into this CMDBUF; 1 means sw has copied data into this CMDBUF + u8 cmdbuf_data_linked; //0 :not linked, 1:linked. + volatile u8 cmdbuf_run_done; //if 0,waiting for CMDBUF finish; if 1, op code in CMDBUF has finished one by one. HANTRO_VCMD_IOCH_WAIT_CMDBUF will check this variable. + u8 cmdbuf_need_remove; // if 0, not need to remove CMDBUF; 1 CMDBUF can be removed if it is not the last CMDBUF; + u32 waited; // if 0, the cmd buf hasn't been waited, otherwise, has been waited + u8 has_end_cmdbuf; //if 1, the last opcode is end opCode. + u8 no_normal_int_cmdbuf; //if 1, JMP will not send normal interrupt. +#ifdef PROCESS_MANAGER + struct process_manager_obj* process_manager_obj; +#endif +}; + +struct hantrovcmd_dev +{ + struct vcmd_config vcmd_core_cfg; //config of each core,such as base addr, irq,etc + u32 core_id; //vcmd core id for driver and sw internal use + u32 sw_cmdbuf_rdy_num; + spinlock_t *spinlock; + //wait_queue_head_t * wait_queue; + //wait_queue_head_t * wait_abort_queue; + bi_list list_manager; + volatile u8 *hwregs; /* IO mem base */ + u32 reg_mirror[ASIC_VCMD_SWREG_AMOUNT]; + u32 duration_without_int; //number of cmdbufs without interrupt. + u8 working_state; + u32 total_exe_time; + u16 status_cmdbuf_id; //used for analyse configuration in cwl. + u32 hw_version_id; /*megvii 0x43421001, later 0x43421102*/ + u32 *vcmd_reg_mem_virtualAddress; //start virtual address of vcmd registers memory of CMDBUF. + ptr_t /*size_t*/ vcmd_reg_mem_busAddress; //start physical address of vcmd registers memory of CMDBUF. + u32 vcmd_reg_mem_size; //size of vcmd registers memory of CMDBUF. +}; + +/* + * Ioctl definitions + */ +#define VCMD_HW_ID 0x4342 + +static struct noncache_mem vcmd_buf_mem_pool = {0}; +static struct noncache_mem vcmd_status_buf_mem_pool = {0}; +static struct noncache_mem vcmd_registers_mem_pool = {0}; + +static u16 cmdbuf_used[TOTAL_DISCRETE_CMDBUF_NUM] = {0}; +static u16 cmdbuf_used_pos = 0; +static u16 cmdbuf_used_residual = 0; + +static struct hantrovcmd_dev *vcmd_manager[MAX_VCMD_TYPE][MAX_VCMD_NUMBER] = {NULL}; +bi_list_node *global_cmdbuf_node[TOTAL_DISCRETE_CMDBUF_NUM] = {NULL}; +#if PROCESS_MANAGER +bi_list global_process_manager = {NULL}; +#endif +static u16 vcmd_position[MAX_VCMD_TYPE] = {0}; +static int vcmd_type_core_num[MAX_VCMD_TYPE] = {0}; + +#define EXECUTING_CMDBUF_ID_ADDR 26 +#define VCMD_EXE_CMDBUF_COUNT 3 + +#define WORKING_STATE_IDLE 0 +#define WORKING_STATE_WORKING 1 +#define CMDBUF_EXE_STATUS_OK 0 +#define CMDBUF_EXE_STATUS_CMDERR 1 +#define CMDBUF_EXE_STATUS_BUSERR 2 + +/*struct semaphore*/ pthread_mutex_t vcmd_reserve_cmdbuf_sem[MAX_VCMD_TYPE]; //for reserve +static void queue_vcmd_init(void *semaphore); +static void queue_vcmd_wakeup(void *semaphore); +static u32 queue_vcmd_wait(void *semaphore); +/***************************TYPE AND FUNCTION DECLARATION****************/ +/* here's all the must remember stuff */ +static int vcmd_reserve_IO(void); +static void vcmd_release_IO(void); +static void vcmd_reset_asic(struct hantrovcmd_dev *dev); +static void vcmd_reset_current_asic(struct hantrovcmd_dev *dev); +static int allocate_cmdbuf(struct noncache_mem *new_cmdbuf_addr, struct noncache_mem *new_status_cmdbuf_addr); +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev, bi_list_node *last_linked_cmdbuf_node); +static void vcmd_start(struct hantrovcmd_dev *dev, bi_list_node *first_linked_cmdbuf_node); +static void create_kernel_process_manager(void); + +static irqreturn_t hantrovcmd_isr(/*int irq, */ void *dev_id); +/* Interrupt */ +/*********************request_irq, disable_irq, enable_irq need to be provided by customer*********************/ +static int RegisterIRQ(i32 i, IRQHandler isr, i32 flag, const char *name, void *data); +static void IntEnableIRQ(u32 irq); +static void IntDisableIRQ(u32 irq); +static void IntClearIRQStatus(u32 irq); +static u32 IntGetIRQStatus(u32 irq); +static inline uint32_t ReadInterruptStatus(void); +/*********************local variable declaration*****************/ +static unsigned long vcmd_sram_base = 0; +static unsigned int vcmd_sram_size = 0; +/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ +static int hantrovcmd_major = 0; +int total_vcmd_core_num = 0; +/* dynamic allocation*/ +static struct hantrovcmd_dev *hantrovcmd_data = NULL; + +//#define VCMD_DEBUG_INTERNAL + +//#define IRQ_SIMULATION + +#ifdef IRQ_SIMULATION +struct timer_manager +{ + u32 core_id; //vcmd core id for driver and sw internal use + u32 timer_id; + + TimerHandle_t * /*struct timer_list * */ timer; +}; +static TimerHandle_t /*struct timer_list */ timer[10000]; +struct timer_manager timer_reserve[10000]; + +#if 0 +static struct timer_list timer0; +static struct timer_list timer1; +#endif +#endif + +//hw_queue can be used for reserve cmdbuf memory +//DECLARE_WAIT_QUEUE_HEAD(vcmd_cmdbuf_memory_wait); +sem_t vcmd_cmdbuf_memory_wait; +//DEFINE_SPINLOCK(vcmd_cmdbuf_alloc_lock); +pthread_mutex_t vcmd_cmdbuf_alloc_lock = PTHREAD_MUTEX_INITIALIZER; +#if PROCESS_MANAGER +//DEFINE_SPINLOCK(vcmd_process_manager_lock); +pthread_mutex_t vcmd_process_manager_lock = PTHREAD_MUTEX_INITIALIZER; +#endif +spinlock_t owner_lock_vcmd[MAX_VCMD_NUMBER]; + +static u8 vcmd_init_flag = 0; + +#ifdef VCMD_POLLING +/*wait_queue_head_t*/ sem_t wait_queue_vcmd[TOTAL_DISCRETE_CMDBUF_NUM /*MAX_VCMD_NUMBER*/]; +/*wait_queue_head_t*/ sem_t abort_queue_vcmd[TOTAL_DISCRETE_CMDBUF_NUM /*MAX_VCMD_NUMBER*/]; +#else +/*wait_queue_head_t*/ SemaphoreHandle_t wait_queue_vcmd[TOTAL_DISCRETE_CMDBUF_NUM /*MAX_VCMD_NUMBER*/]; +/*wait_queue_head_t*/ SemaphoreHandle_t abort_queue_vcmd[TOTAL_DISCRETE_CMDBUF_NUM /*MAX_VCMD_NUMBER*/]; +#endif + +/* mc wait queue, used in wait_cmdbuf_ready with ANY_CMDBUF_ID. */ +static /*wait_queue_head_t*/ /*sem_t*/SemaphoreHandle_t mc_wait_queue; + +#if 0 +/*allocate non-cacheable DMA memory*/ +#define DRIVER_NAME_HANTRO_NON_CACH_MEM "non_cach_memory" + +static struct platform_device *noncachable_mem_dev = NULL; + + +static const struct platform_device_info hantro_platform_info = { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + + +static int hantro_noncachable_mem_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + vcmd_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_status_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_status_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + return 0; +} + +static int hantro_noncachable_mem_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dma_free_coherent(dev,vcmd_buf_mem_pool.size,vcmd_buf_mem_pool.virtualAddress,vcmd_buf_mem_pool.busAddress); + dma_free_coherent(dev,vcmd_status_buf_mem_pool.size,vcmd_status_buf_mem_pool.virtualAddress,vcmd_status_buf_mem_pool.busAddress); + + return 0; +} + + +static const struct platform_device_id hantro_noncachable_mem_platform_ids[]={ + { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + }, + {/* sentinel */}, +}; + + +static const struct of_device_id hantro_of_match[]={ + { + .compatible = "thead,light-vc8000d", + }, + {/* sentinel */}, +}; + +static struct platform_driver hantro_noncachable_mem_platform_driver = { + .probe = hantro_noncachable_mem_probe, + .remove = hantro_noncachable_mem_remove, + .driver ={ + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .owner = THIS_MODULE, + .of_match_table = hantro_of_match, + }, + .id_table = hantro_noncachable_mem_platform_ids, +}; + +static void init_vcmd_non_cachable_memory_allocate(void) +{ + + /*create device: This will create a {struct platform_device}, It has a member dev, which is a {struct device} */ + noncachable_mem_dev = platform_device_register_full(&hantro_platform_info); + + /*when this function is called, the .probe callback is invoked.*/ + platform_driver_register(&hantro_noncachable_mem_platform_driver); + + +} + +static void release_vcmd_non_cachable_memory(void) +{ + + /* when this fucntion is called, .remove callback will be invoked. use it to clean up all resources allocated in .probe.*/ + platform_driver_unregister(&hantro_noncachable_mem_platform_driver); + + /*destroy the device*/ + platform_device_unregister(noncachable_mem_dev); +} +#endif + +void PrintInstr(u32 i, u32 instr, u32 *size) +{ + if ((instr & 0xF8000000) == OPCODE_WREG) + { + int length = ((instr >> 16) & 0x3FF); + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s %s %d 0x%x]\n", i, instr, "WREG", ((instr >> 26) & 0x1) ? "FIX" : "", + length, (instr & 0xFFFF)); + *size = ((length + 2) >> 1) << 1; + } + else if ((instr & 0xF8000000) == OPCODE_END) + { + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s]\n", i, instr, "END"); + *size = 2; + } + else if ((instr & 0xF8000000) == OPCODE_NOP) + { + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s]\n", i, instr, "NOP"); + *size = 2; + } + else if ((instr & 0xF8000000) == OPCODE_RREG) + { + int length = ((instr >> 16) & 0x3FF); + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s %s %d 0x%x]\n", i, instr, "RREG", ((instr >> 26) & 0x1) ? "FIX" : "", + length, (instr & 0xFFFF)); + *size = 4; + } + else if ((instr & 0xF8000000) == OPCODE_JMP) + { + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s %s %s]\n", i, instr, "JMP", ((instr >> 26) & 0x1) ? "RDY" : "", + ((instr >> 25) & 0x1) ? "IE" : ""); + *size = 4; + } + else if ((instr & 0xF8000000) == OPCODE_STALL) + { + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s %s 0x%x]\n", i, instr, "STALL", ((instr >> 26) & 0x1) ? "IM" : "", + (instr & 0xFFFF)); + *size = 2; + } + else if ((instr & 0xF8000000) == OPCODE_CLRINT) + { + printk(KERN_INFO "current cmdbuf data %d = 0x%08x => [%s %d 0x%x]\n", i, instr, "CLRINT", (instr >> 25) & 0x3, + (instr & 0xFFFF)); + *size = 2; + } + else + *size = 1; +} + +/**********************************************************************************************************\ +*cmdbuf object management +\***********************************************************************************************************/ +static struct cmdbuf_obj *create_cmdbuf_obj(void) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + cmdbuf_obj = vmalloc(sizeof(struct cmdbuf_obj)); + if (cmdbuf_obj == NULL) + { + PDEBUG("%s\n", "vmalloc for cmdbuf_obj fail!"); + return cmdbuf_obj; + } + memset(cmdbuf_obj, 0, sizeof(struct cmdbuf_obj)); + return cmdbuf_obj; +} + +static void free_cmdbuf_obj(struct cmdbuf_obj *cmdbuf_obj) +{ + if (cmdbuf_obj == NULL) + { + PDEBUG("%s\n", "remove_cmdbuf_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(cmdbuf_obj); + return; +} +static void free_cmdbuf_mem(u16 cmdbuf_id) +{ + unsigned long flags; + + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + cmdbuf_used[cmdbuf_id] = 0; + cmdbuf_used_residual += 1; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); +#if 1 + sem_post(&vcmd_cmdbuf_memory_wait); + PDEBUG("Release cmdbuf_id [%d], remain cmd buffer %d\n", cmdbuf_id, vcmd_cmdbuf_memory_wait.value); +#else + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); +#endif +} + +static bi_list_node *create_cmdbuf_node(void) +{ + bi_list_node *current_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + struct noncache_mem new_cmdbuf_addr = {0}; + struct noncache_mem new_status_cmdbuf_addr = {0}; +#if 1 + if (sem_wait(&vcmd_cmdbuf_memory_wait)) + { //wait cmdbuf, need to sem_wait until some cmdbuf was posted + PDEBUG("%s\n", "wait vcmd_cmdbuf_memory fail!"); + return NULL; + } + allocate_cmdbuf(&new_cmdbuf_addr, &new_status_cmdbuf_addr); +#else + if (wait_event_interruptible(vcmd_cmdbuf_memory_wait, allocate_cmdbuf(&new_cmdbuf_addr, &new_status_cmdbuf_addr))) + return NULL; +#endif + cmdbuf_obj = create_cmdbuf_obj(); + if (cmdbuf_obj == NULL) + { + PDEBUG("%s\n", "create_cmdbuf_obj fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); + return NULL; + } + cmdbuf_obj->cmdbuf_busAddress = new_cmdbuf_addr.busAddress; + cmdbuf_obj->cmdbuf_virtualAddress = new_cmdbuf_addr.virtualAddress; + cmdbuf_obj->cmdbuf_size = new_cmdbuf_addr.size; + cmdbuf_obj->cmdbuf_id = new_cmdbuf_addr.cmdbuf_id; + cmdbuf_obj->status_busAddress = new_status_cmdbuf_addr.busAddress; + cmdbuf_obj->status_virtualAddress = new_status_cmdbuf_addr.virtualAddress; + cmdbuf_obj->status_size = new_status_cmdbuf_addr.size; + current_node = bi_list_create_node(); + if (current_node == NULL) + { + PDEBUG("%s\n", "bi_list_create_node fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); + free_cmdbuf_obj(cmdbuf_obj); + return NULL; + } + current_node->data = (void *)cmdbuf_obj; + current_node->next = NULL; + current_node->previous = NULL; + return current_node; +} +static void free_cmdbuf_node(bi_list_node *cmdbuf_node) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + if (cmdbuf_node == NULL) + { + PDEBUG("%s\n", "remove_cmdbuf_node NULL"); + return; + } + cmdbuf_obj = (struct cmdbuf_obj *)cmdbuf_node->data; + //free cmdbuf mem in pool + free_cmdbuf_mem(cmdbuf_obj->cmdbuf_id); + + //free struct cmdbuf_obj + free_cmdbuf_obj(cmdbuf_obj); + //free current cmdbuf_node entity. + bi_list_free_node(cmdbuf_node); + return; +} + +//just remove, not free the node. +static bi_list_node *remove_cmdbuf_node_from_list(bi_list *list, bi_list_node *cmdbuf_node) +{ + if (cmdbuf_node == NULL) + { + PDEBUG("%s\n", "remove_cmdbuf_node_from_list NULL"); + return NULL; + } + if (cmdbuf_node->next) + { + bi_list_remove_node(list, cmdbuf_node); + return cmdbuf_node; + } + else + { + //the last one, should not be removed. + return NULL; + } +} +//calculate executing_time of each vcmd +static u32 calculate_executing_time_after_node(bi_list_node *exe_cmdbuf_node) +{ + u32 time_run_all = 0; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; + while (1) + { + if (exe_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp = (struct cmdbuf_obj *)exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} +static u32 calculate_executing_time_after_node_high_priority(bi_list_node *exe_cmdbuf_node) +{ + u32 time_run_all = 0; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; + if (exe_cmdbuf_node == NULL) + return time_run_all; + cmdbuf_obj_temp = (struct cmdbuf_obj *)exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + while (1) + { + if (exe_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp = (struct cmdbuf_obj *)exe_cmdbuf_node->data; + if (cmdbuf_obj_temp->priority == CMDBUF_PRIORITY_NORMAL) + break; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} + +/**********************************************************************************************************\ +*cmdbuf pool management +\***********************************************************************************************************/ +static int allocate_cmdbuf(struct noncache_mem *new_cmdbuf_addr, struct noncache_mem *new_status_cmdbuf_addr) +{ + unsigned long flags; + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + if (cmdbuf_used_residual == 0) + { + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + //no empty cmdbuf + return 0; + } + //there is one cmdbuf at least + while (1) + { + if (cmdbuf_used[cmdbuf_used_pos] == 0 && (global_cmdbuf_node[cmdbuf_used_pos] == NULL)) + { + cmdbuf_used[cmdbuf_used_pos] = 1; + cmdbuf_used_residual -= 1; + new_cmdbuf_addr->virtualAddress = vcmd_buf_mem_pool.virtualAddress + cmdbuf_used_pos * CMDBUF_MAX_SIZE / 4; + new_cmdbuf_addr->busAddress = vcmd_buf_mem_pool.busAddress + cmdbuf_used_pos * CMDBUF_MAX_SIZE; + new_cmdbuf_addr->size = CMDBUF_MAX_SIZE; + new_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + new_status_cmdbuf_addr->virtualAddress = vcmd_status_buf_mem_pool.virtualAddress + cmdbuf_used_pos * CMDBUF_MAX_SIZE / 4; + new_status_cmdbuf_addr->busAddress = vcmd_status_buf_mem_pool.busAddress + cmdbuf_used_pos * CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->size = CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + cmdbuf_used_pos++; + if (cmdbuf_used_pos >= TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos = 0; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + return 1; + } + else + { + cmdbuf_used_pos++; + if (cmdbuf_used_pos >= TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos = 0; + } + } + return 0; +} + +static bi_list_node *get_cmdbuf_node_in_list_by_addr(/*size_t*/ ptr_t cmdbuf_addr, bi_list *list) +{ + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + new_cmdbuf_node = list->head; + while (1) + { + if (new_cmdbuf_node == NULL) + return NULL; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) <= cmdbuf_addr) && (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr + cmdbuf_obj->cmdbuf_size) > cmdbuf_addr))) + { + return new_cmdbuf_node; + } + new_cmdbuf_node = new_cmdbuf_node->next; + } + return NULL; +} + +static int wait_abort_rdy(struct hantrovcmd_dev *dev) +{ + return dev->working_state == WORKING_STATE_IDLE; +} + + +static inline void increase_vcmd_position(u32 module_type) +{ + vcmd_position[module_type]++; + if (vcmd_position[module_type] >= vcmd_type_core_num[module_type]) + vcmd_position[module_type] = 0; +} + +static inline int vcmd_is_free(struct hantrovcmd_dev *dev) +{ + bi_list *list = NULL; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; + + list = &dev->list_manager; + if (dev->list_manager.tail == NULL) + { + //no vcmd_buf is linked + return 1; + } + + cmdbuf_obj_temp = (struct cmdbuf_obj *)list->tail->data; + if(cmdbuf_obj_temp->cmdbuf_run_done == 1) + { + //last vcmd_buf is done. + return 1; + } + + return 0; +} + +static int vcmd_get_executing_time(struct hantrovcmd_dev *dev, int priority) +{ + bi_list *list = NULL; + bi_list_node *curr_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; + ptr_t exe_cmdbuf_addr = 0; + unsigned long flags = 0; + u32 cmdbuf_id = 0; + + list = &dev->list_manager; + //read executing cmdbuf address + if (dev->hw_version_id <= HW_ID_1_0_C) + { + exe_cmdbuf_addr = VCMDGetAddrRegisterValue((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR); + + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node = get_cmdbuf_node_in_list_by_addr(exe_cmdbuf_addr, list); + } + else + { + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress + EXECUTING_CMDBUF_ID_ADDR + 1); + if (cmdbuf_id >= TOTAL_DISCRETE_CMDBUF_NUM || cmdbuf_id == 0) + { + printk(KERN_ERR "cmdbuf_id greater than the ceiling !!\n"); + return -1; + } + + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (curr_cmdbuf_node == NULL) + { + curr_cmdbuf_node = list->head; + while (1) + { + if (curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp = (struct cmdbuf_obj *)curr_cmdbuf_node->data; + if (cmdbuf_obj_temp->cmdbuf_data_linked && cmdbuf_obj_temp->cmdbuf_run_done == 0) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + } + } + + //calculate total execute time of this device + if(priority == CMDBUF_PRIORITY_HIGH) + dev->total_exe_time = calculate_executing_time_after_node_high_priority(curr_cmdbuf_node); + else + dev->total_exe_time = calculate_executing_time_after_node(curr_cmdbuf_node); + + spin_unlock_irqrestore(dev->spinlock, flags); + return 0; +} + +static int select_vcmd(bi_list_node *new_cmdbuf_node) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + bi_list_node *curr_cmdbuf_node = NULL; + bi_list *list = NULL; + struct hantrovcmd_dev *dev = NULL; + struct hantrovcmd_dev *smallest_dev = NULL; + u32 executing_time = 0xffff; + int counter = 0; + unsigned long flags = 0; + u32 hw_rdy_cmdbuf_num = 0; + /*size_t*/ ptr_t exe_cmdbuf_addr = 0; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; + u32 cmdbuf_id = 0; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + int ret; + + //if there is an empty vcmd + for(counter=0; countermodule_type]; counter++) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + increase_vcmd_position(cmdbuf_obj->module_type); + + list = &dev->list_manager; + + spin_lock_irqsave(dev->spinlock, flags); + + if (vcmd_is_free(dev)) + { + bi_list_insert_node_tail(list, new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + + spin_unlock_irqrestore(dev->spinlock, flags); + } + + //another case, tail = executing node, and vcmd=pend state (finish but not generate interrupt) + for(counter=0; countermodule_type]; counter++) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + increase_vcmd_position(cmdbuf_obj->module_type); + + list = &dev->list_manager; + //read executing cmdbuf address + if (dev->hw_version_id <= HW_ID_1_0_C) + hw_rdy_cmdbuf_num = vcmd_get_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXE_CMDBUF_COUNT); + else + { + hw_rdy_cmdbuf_num = *(dev->vcmd_reg_mem_virtualAddress + VCMD_EXE_CMDBUF_COUNT); + if (hw_rdy_cmdbuf_num != dev->sw_cmdbuf_rdy_num) + hw_rdy_cmdbuf_num += 1; + } + + if (dev->sw_cmdbuf_rdy_num == hw_rdy_cmdbuf_num) + { + spin_lock_irqsave(dev->spinlock, flags); + bi_list_insert_node_tail(list, new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + } + + //there is no idle vcmd,if low priority,calculate exe time, select the least one. + // or if high priority, calculate the exe time, select the least one and abort it. + executing_time = 0xffffffff; + for(counter=0; countermodule_type]; counter++) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + increase_vcmd_position(cmdbuf_obj->module_type); + + ret = vcmd_get_executing_time(dev, cmdbuf_obj->priority); + if(ret) + return ret; + + if (dev->total_exe_time <= executing_time) + { + executing_time = dev->total_exe_time; + smallest_dev = dev; + } + } + + if (cmdbuf_obj->priority == CMDBUF_PRIORITY_NORMAL) + { + //insert list + list = &smallest_dev->list_manager; + spin_lock_irqsave(smallest_dev->spinlock, flags); + bi_list_insert_node_tail(list, new_cmdbuf_node); + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + cmdbuf_obj->core_id = smallest_dev->core_id; + return 0; + } + else + { + //CMDBUF_PRIORITY_HIGH + + //abort the vcmd and wait + vcmd_write_register_value((const void *)smallest_dev->hwregs, smallest_dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 0); + +#if 1 +#if 1 + //use yield + while(1) + { + if (cmdbuf_obj->cmdbuf_run_done) + break; + sched_yield(); + } +#else + queue_vcmd_wait(&abort_queue_vcmd[cmdbuf_obj->cmdbuf_id]); +#endif +#else + if (wait_event_interruptible(*smallest_dev->wait_abort_queue, wait_abort_rdy(dev))) + return -ERESTARTSYS; +#endif + //need to select inserting position again because hw maybe have run to the next node. + //CMDBUF_PRIORITY_HIGH + spin_lock_irqsave(smallest_dev->spinlock, flags); + curr_cmdbuf_node = smallest_dev->list_manager.head; + while (1) + { + //if list is empty or tail,insert to tail + if (curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp = (struct cmdbuf_obj *)curr_cmdbuf_node->data; + //if find the first node which priority is normal, insert node prior to the node + if ((cmdbuf_obj_temp->priority == CMDBUF_PRIORITY_NORMAL) && (cmdbuf_obj_temp->cmdbuf_run_done == 0)) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + bi_list_insert_node_before(list, curr_cmdbuf_node, new_cmdbuf_node); + cmdbuf_obj->core_id = smallest_dev->core_id; + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + + return 0; + } + return 0; +} + +#if PROCESS_MANAGER +static int wait_process_resource_rdy(struct process_manager_obj *process_manager_obj) +{ + return process_manager_obj->total_exe_time <= PROCESS_MAX_SUM_OF_IMAGE_SIZE; +} +#endif + +static long reserve_cmdbuf(/*struct file */ int filp, struct exchange_parameter *input_para) +{ + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + bi_list_node *process_manager_node = NULL; +#if PROCESS_MANAGER + struct process_manager_obj *process_manager_obj = NULL; +#endif + unsigned long flags = 0; + input_para->cmdbuf_id = 0; + + if (input_para->cmdbuf_size > CMDBUF_MAX_SIZE) + { + return -1; + } + PDEBUG("reserve cmdbuf filp %p\n", (void *)filp); + +#if PROCESS_MANAGER + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while (1) + { + if (process_manager_node == NULL) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR process_manager_node !!\n"); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + return -1; + } + process_manager_obj = (struct process_manager_obj *)process_manager_node->data; + PDEBUG("reserve loop: node %p, filp %p\n", (void *)process_manager_node, + (void *)process_manager_obj->filp); + if (filp == process_manager_obj->filp) + { + spin_lock_irqsave(&process_manager_obj->spinlock, flags); + process_manager_obj->total_exe_time += input_para->executing_time; + spin_unlock_irqrestore(&process_manager_obj->spinlock, flags); + break; + } + process_manager_node = process_manager_node->next; + } + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + + if (wait_event_interruptible(process_manager_obj->wait_queue, wait_process_resource_rdy(process_manager_obj))) + return -1; +#endif + + new_cmdbuf_node = create_cmdbuf_node(); + if (new_cmdbuf_node == NULL) + return -1; + + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + cmdbuf_obj->module_type = input_para->module_type; + cmdbuf_obj->priority = input_para->priority; + cmdbuf_obj->executing_time = input_para->executing_time; + cmdbuf_obj->cmdbuf_size = CMDBUF_MAX_SIZE; + input_para->cmdbuf_size = CMDBUF_MAX_SIZE; + cmdbuf_obj->filp = filp; + +#if PROCESS_MANAGER + cmdbuf_obj->process_manager_obj = process_manager_obj; +#endif + + input_para->cmdbuf_id = cmdbuf_obj->cmdbuf_id; + global_cmdbuf_node[input_para->cmdbuf_id] = new_cmdbuf_node; + + return 0; +} + +static long release_cmdbuf(/*struct file */ int filp, u16 cmdbuf_id) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + bi_list_node *last_cmdbuf_node = NULL; + bi_list_node *new_cmdbuf_node = NULL; + bi_list *list = NULL; + u32 module_type; + + unsigned long flags; + struct hantrovcmd_dev *dev = NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->filp != filp) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + module_type = cmdbuf_obj->module_type; + //TODO + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[module_type])) + return -ERESTARTSYS; + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + + //spin_lock_irqsave(dev->spinlock, flags); + list = &dev->list_manager; + cmdbuf_obj->cmdbuf_need_remove = 1; + last_cmdbuf_node = new_cmdbuf_node->previous; + while (1) + { + //remove current node + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_need_remove == 1) + { + new_cmdbuf_node = remove_cmdbuf_node_from_list(list, new_cmdbuf_node); + if (new_cmdbuf_node) + { + //free node + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; +#if PROCESS_MANAGER + if (cmdbuf_obj->process_manager_obj) + { + spin_lock_irqsave(&cmdbuf_obj->process_manager_obj->spinlock, flags); + cmdbuf_obj->process_manager_obj->total_exe_time -= cmdbuf_obj->executing_time; + spin_unlock_irqrestore(&cmdbuf_obj->process_manager_obj->spinlock, flags); + wake_up_interruptible_all(&cmdbuf_obj->process_manager_obj->wait_queue); + } +#endif + free_cmdbuf_node(new_cmdbuf_node); + } + } + if (last_cmdbuf_node == NULL) + break; + new_cmdbuf_node = last_cmdbuf_node; + last_cmdbuf_node = new_cmdbuf_node->previous; + } + //spin_unlock_irqrestore(dev->spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[module_type]); + return 0; +} +static long release_cmdbuf_node(bi_list *list, bi_list_node *cmdbuf_node) +{ + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node = cmdbuf_node; + if (new_cmdbuf_node == NULL) + return -1; + //remove node from list + new_cmdbuf_node = remove_cmdbuf_node_from_list(list, new_cmdbuf_node); + if (new_cmdbuf_node) + { + //free node + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + return 0; + } + return 1; +} + +static long release_cmdbuf_node_cleanup(bi_list *list) +{ + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + while (1) + { + new_cmdbuf_node = list->head; + if (new_cmdbuf_node == NULL) + return 0; + //remove node from list + bi_list_remove_node(list, new_cmdbuf_node); + //free node + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + } + return 0; +} + +static bi_list_node *find_last_linked_cmdbuf(bi_list_node *current_node) +{ + bi_list_node *new_cmdbuf_node = current_node; + bi_list_node *last_cmdbuf_node; + struct cmdbuf_obj *cmdbuf_obj = NULL; + if (current_node == NULL) + return NULL; + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + while (1) + { + if (new_cmdbuf_node == NULL) + return last_cmdbuf_node; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_data_linked) + { + return new_cmdbuf_node; + } + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + return NULL; +} +static long link_and_run_cmdbuf(/*struct file */ int filp, struct exchange_parameter *input_para) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + bi_list_node *new_cmdbuf_node = NULL; + bi_list_node *last_cmdbuf_node = NULL; + u32 *jmp_addr = NULL; + u32 opCode = 0; + u32 tempOpcode = 0; + u32 record_last_cmdbuf_rdy_num = 0; + struct hantrovcmd_dev *dev = NULL; + unsigned long flags = 0; + int return_value = 0; + u16 cmdbuf_id = input_para->cmdbuf_id; + + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->filp != filp) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj->cmdbuf_data_loaded = 1; + cmdbuf_obj->cmdbuf_size = input_para->cmdbuf_size; + cmdbuf_obj->waited = 0; +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, inst = 0, size = 0; + printk(KERN_INFO "vcmd link, current cmdbuf content\n"); + for (i = 0; i < cmdbuf_obj->cmdbuf_size / 4; i++) + { + if (i == inst) + { + PrintInstr(i, *(cmdbuf_obj->cmdbuf_virtualAddress + i), &size); + inst += size; + } + else + { + printk(KERN_INFO "current cmdbuf data %d = 0x%x\n", i, *(cmdbuf_obj->cmdbuf_virtualAddress + i)); + } + } + } +#endif + //test nop and end opcode, then assign value. + cmdbuf_obj->has_end_cmdbuf = 0; //0: has jmp opcode,1 has end code + cmdbuf_obj->no_normal_int_cmdbuf = 0; //0: interrupt when JMP,1 not interrupt when JMP + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size / 4); + opCode = tempOpcode = *(jmp_addr - 4); + opCode >>= 27; + opCode <<= 27; + //we can't identify END opcode or JMP opcode, so we don't support END opcode in control sw and driver. + if (opCode == OPCODE_JMP) + { + //jmp + opCode = tempOpcode; + opCode &= 0x02000000; + if (opCode == JMP_IE_1) + { + cmdbuf_obj->no_normal_int_cmdbuf = 0; + } + else + { + cmdbuf_obj->no_normal_int_cmdbuf = 1; + } + } + else + { + //not support other opcode + return -1; + } + + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type])) + return -ERESTARTSYS; + + return_value = select_vcmd(new_cmdbuf_node); + if (return_value) + return return_value; + + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + input_para->core_id = cmdbuf_obj->core_id; + printk(KERN_INFO "Allocate cmd buffer [%d] to core [%d], remain cmd buffer %d\n", cmdbuf_id, input_para->core_id, vcmd_cmdbuf_memory_wait.value); + //set ddr address for vcmd registers copy. + if (dev->hw_version_id > HW_ID_1_0_C) + { + //read vcmd executing register into ddr memory. + //now core id is got and output ddr address of vcmd register can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress; + if (sizeof(ptr_t) == 8) + { + *(jmp_addr + 2) = (u32)((u64)(dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR + 1) * 4) >> 32); + } + else + { + *(jmp_addr + 2) = 0; + } + *(jmp_addr + 1) = (u32)((dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR + 1) * 4)); + + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size / 4); + //read vcmd all registers into ddr memory. + //now core id is got and output ddr address of vcmd registers can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + if (sizeof(ptr_t) == 8) + { + *(jmp_addr - 6) = (u32)((u64)dev->vcmd_reg_mem_busAddress >> 32); + } + else + { + *(jmp_addr - 6) = 0; + } + *(jmp_addr - 7) = (u32)(dev->vcmd_reg_mem_busAddress); + } + //start to link and/or run + spin_lock_irqsave(dev->spinlock, flags); + last_cmdbuf_node = find_last_linked_cmdbuf(new_cmdbuf_node); + record_last_cmdbuf_rdy_num = dev->sw_cmdbuf_rdy_num; + vcmd_link_cmdbuf(dev, last_cmdbuf_node); + if (dev->working_state == WORKING_STATE_IDLE) + { + //run + vcmd_start(dev, last_cmdbuf_node); + } + else + { + //just update cmdbuf ready number + if (record_last_cmdbuf_rdy_num != dev->sw_cmdbuf_rdy_num) { + _Pragma("flush_memory"); + xthal_dcache_all_writeback_inv(); + _Pragma("flush_memory"); + vcmd_write_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_RDY_CMDBUF_COUNT, dev->sw_cmdbuf_rdy_num); + } + } + + spin_unlock_irqrestore(dev->spinlock, flags); + + up(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type]); + + return 0; +} + +/******************************************************************************/ +static int check_cmdbuf_irq(struct hantrovcmd_dev *dev, struct cmdbuf_obj *cmdbuf_obj, u32 *irq_status_ret) +{ + + int rdy = 0; + unsigned long flags; + spin_lock_irqsave(dev->spinlock, flags); + if (cmdbuf_obj->cmdbuf_run_done) + { + rdy = 1; + *irq_status_ret = cmdbuf_obj->executing_status; //need to decide how to assign this variable + } + spin_unlock_irqrestore(dev->spinlock, flags); + return rdy; +} + +/******************************************************************************/ +static int check_mc_cmdbuf_irq(/*struct file * */ int filp, struct cmdbuf_obj *cmdbuf_obj, u32 *irq_status_ret) +{ + int k; + bi_list_node *new_cmdbuf_node = NULL; + struct hantrovcmd_dev *dev = NULL; + + for(k=0;kdata; + if(!cmdbuf_obj || cmdbuf_obj->filp != filp) + continue; + + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + if (check_cmdbuf_irq(dev, cmdbuf_obj, irq_status_ret) == 1) { + /* Return cmdbuf_id when ANY_CMDBUF_ID is used. */ + if (!cmdbuf_obj->waited) { + *irq_status_ret = cmdbuf_obj->cmdbuf_id; + cmdbuf_obj->waited = 1; + return 1; + } + } + } + // check no job + //if (k == TOTAL_DISCRETE_CMDBUF_NUM) + // return 1; + return 0; +} + +#ifdef IRQ_SIMULATION +void get_random_bytes(void *buf, int nbytes); +#if 0 +void hantrovcmd_trigger_irq_0(struct timer_list* timer) +{ + PDEBUG("trigger core 0 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[0]); +} +void hantrovcmd_trigger_irq_1(struct timer_list* timer) +{ + PDEBUG("trigger core 1 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[1]); +} +#else +void hantrovcmd_trigger_irq(TimerHandle_t * /*struct timer_list * */ timer) +{ + + u32 timer_id = 0; + u32 core_id = 0; + u32 i; + for (i = 0; i < 10000; i++) + { + if (timer_reserve[i].timer == timer) + { + timer_id = timer_reserve[i].timer_id; + core_id = timer_reserve[i].core_id; + break; + } + } + PDEBUG("trigger core 0 irq\n"); + hantrovcmd_isr(/*core_id,*/ (void *)&hantrovcmd_data[core_id]); + + //del_timer(timer); + xTimerDelete(timer); + timer_reserve[timer_id].timer = NULL; +} + +#endif +#endif + +static unsigned int wait_cmdbuf_ready(/*struct file */ int filp, u16 cmdbuf_id, u32 *irq_status_ret) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + bi_list_node *new_cmdbuf_node = NULL; + struct hantrovcmd_dev *dev = NULL; + if(cmdbuf_id != ANY_CMDBUF_ID) { + PDEBUG("wait_cmdbuf_ready cmdbuf_id [%d]\n", cmdbuf_id); + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->filp != filp) + { + //should not happen + printk(KERN_ERR "hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; +#ifdef IRQ_SIMULATION + { + u32 random_num; + //get_random_bytes(&random_num, sizeof(u32)); + random_num = (u32)((u64)100 * cmdbuf_obj->executing_time / (4096 * 2160) + 50); + PDEBUG("random_num=%d\n", random_num); +#if 0 + /*init a timer to trigger irq*/ + if (cmdbuf_obj->core_id==0) + { + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + timer_setup(&timer0,hantrovcmd_trigger_irq_0,0); + timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer0); + } + + if (cmdbuf_obj->core_id==1) + { + //init_timer(&timer1); + //timer1.function = hantrovcmd_trigger_irq_1; + timer_setup(&timer1,hantrovcmd_trigger_irq_1,0); + timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer1); + } +#else + { + u32 i; + /*struct timer_list */ TimerHandle_t *temp_timer = NULL; + for (i = 0; i < 10000; i++) + { + if (timer_reserve[i].timer == NULL) + { + timer_reserve[i].timer_id = i; + timer_reserve[i].core_id = cmdbuf_obj->core_id; + temp_timer = timer_reserve[i].timer = &timer[i]; + break; + } + } + //if (cmdbuf_obj->core_id==0) + { +#if 1 + temp_timer = xTimerCreate("Timer", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, hantrovcmd_trigger_irq); + if (temp_timer == NULL) + { + /* The timer was not created. */ + } + else if (xTimerStart(temp_timer, 0) != pdPASS) + { + /* The timer could not be set into the Active state. */ + } + +#else + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + + timer_setup(temp_timer, hantrovcmd_trigger_irq, 0); + temp_timer->expires = jiffies + random_num * HZ / 10; //the expires time is 1s + add_timer(temp_timer); +#endif + } + } +#endif + } +#endif +#if 1 +#if 1 + //use yield + while(1) + { + if (cmdbuf_obj->cmdbuf_run_done) + break; + sched_yield(); + } +#else + //wait a wait_queue + if (queue_vcmd_wait(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id])) + { + PDEBUG("wait vcmd_wait_queue failed\n"); + //abort the vcmd + vcmd_write_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 0); + return -ERESTARTSYS; + } +#endif +#else + if (wait_event_interruptible(*dev->wait_queue, check_cmdbuf_irq(dev, cmdbuf_obj, irq_status_ret))) + { + PDEBUG("vcmd_wait_queue_0 interrupted\n"); + //abort the vcmd + vcmd_write_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 0); + return -ERESTARTSYS; + } +#endif + } else { + /*if (check_mc_cmdbuf_irq(filp, cmdbuf_obj, irq_status_ret)) + return 0; + if (wait_event_interruptible(mc_wait_queue, check_mc_cmdbuf_irq(filp, cmdbuf_obj, irq_status_ret))) + { + PDEBUG("multicore wait_queue interrupted\n"); + return -ERESTARTSYS; + }*/ + + while(1) { + if (check_mc_cmdbuf_irq(filp, cmdbuf_obj, irq_status_ret)) { + return 0; + } + + queue_vcmd_wait(&mc_wait_queue); + } + } + + return 0; +} + +long hantrovcmd_ioctl(int filp, unsigned int cmd, void *arg) +{ + int err = 0; + + PDEBUG("ioctl cmd 0x%08x, filp 0x%x\n", cmd, filp); + /* + * extract the type and number bitfields, and don't encode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != HANTRO_VCMD_IOC_MAGIC) + return -ENOTTY; + if ((_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_VCMD_IOC_MAXNR)) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch (cmd) + { + case HANTRO_VCMD_IOCH_GET_CMDBUF_PARAMETER: + { + struct cmdbuf_mem_parameter local_cmdbuf_mem_data = {NULL, 0}; + PDEBUG("VCMD-Get VCMD CMDBUF Parameter. \n"); + local_cmdbuf_mem_data.cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.phy_status_cmdbuf_addr = vcmd_status_buf_mem_pool.busAddress; + local_cmdbuf_mem_data.phy_cmdbuf_addr = vcmd_buf_mem_pool.busAddress; + local_cmdbuf_mem_data.base_ddr_addr = base_ddr_addr; + copy_to_user((struct cmdbuf_mem_parameter *)arg, &local_cmdbuf_mem_data, sizeof(struct cmdbuf_mem_parameter)); + break; + } + case HANTRO_VCMD_IOCH_GET_VCMD_PARAMETER: + { + struct config_parameter input_para = {0}; + PDEBUG("VCMD-Get VCMD Config Parameter. \n"); + copy_from_user(&input_para, (struct config_parameter *)arg, sizeof(struct config_parameter)); + if (vcmd_type_core_num[input_para.module_type]) + { + input_para.submodule_main_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr; + input_para.submodule_dec400_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_dec400_addr; + input_para.submodule_L2Cache_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr; + input_para.submodule_MMU_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_MMU_addr; + input_para.config_status_cmdbuf_id = vcmd_manager[input_para.module_type][0]->status_cmdbuf_id; + input_para.vcmd_hw_version_id = vcmd_manager[input_para.module_type][0]->hw_version_id; + input_para.vcmd_core_num = vcmd_type_core_num[input_para.module_type]; + } + else + { + input_para.submodule_main_addr = 0xffff; + input_para.submodule_dec400_addr = 0xffff; + input_para.submodule_L2Cache_addr = 0xffff; + input_para.submodule_MMU_addr = 0xffff; + input_para.config_status_cmdbuf_id = 0; + input_para.vcmd_core_num = 0; + input_para.vcmd_hw_version_id = HW_ID_1_0_C; + } + copy_to_user((struct config_parameter *)arg, &input_para, sizeof(struct config_parameter)); + break; + } + case HANTRO_VCMD_IOCH_RESERVE_CMDBUF: + { + int ret = 0; + struct exchange_parameter input_para = {0}; + PDEBUG("VCMD-Reserve CMDBUF. \n"); + copy_from_user(&input_para, (struct exchange_parameter *)arg, sizeof(struct exchange_parameter)); + ret = reserve_cmdbuf(filp, &input_para); + if (ret == 0) + copy_to_user((struct exchange_parameter *)arg, &input_para, sizeof(struct exchange_parameter)); + return ret; + } + + case HANTRO_VCMD_IOCH_LINK_RUN_CMDBUF: + { + struct exchange_parameter input_para = {0}; + long retVal = 0; + copy_from_user(&input_para, (struct exchange_parameter *)arg, sizeof(struct exchange_parameter)); + + PDEBUG("VCMD-Link and Run CMDBUF. \n"); + retVal = link_and_run_cmdbuf(filp, &input_para); + copy_to_user((struct exchange_parameter *)arg, &input_para, sizeof(struct exchange_parameter)); + return retVal; + break; + } + + case HANTRO_VCMD_IOCH_WAIT_CMDBUF: + { + u16 cmdbuf_id = 0; + unsigned int tmp = 0; + u32 irq_status_ret = 0; + __get_user(cmdbuf_id, (u16 *)arg); + /*high 16 bits are core id, low 16 bits are cmdbuf_id*/ + + PDEBUG("VCMD-Wait for CMDBUF Finishing. \n"); + tmp = wait_cmdbuf_ready(filp, cmdbuf_id, &irq_status_ret); + cmdbuf_id = (u16)irq_status_ret; + if (tmp == 0) + { + __put_user(cmdbuf_id, (u16 *)arg); + return tmp; //return core_id + } + else + { + __put_user(0, (u16 *)arg); + return -1; + } + + break; + } + case HANTRO_VCMD_IOCH_RELEASE_CMDBUF: + { + u16 cmdbuf_id = 0; + __get_user(cmdbuf_id, (u16 *)arg); + /*16 bits are cmdbuf_id*/ + + PDEBUG("VCMD-Release CMDBUF. \n"); + release_cmdbuf(filp, cmdbuf_id); + return 0; + break; + } + case HANTRO_VCMD_IOCH_POLLING_CMDBUF: + { + u16 core_id = 0; + __get_user(core_id, (u16 *)arg); + /*16 bits are cmdbuf_id*/ + if (core_id >= total_vcmd_core_num) + return -1; + hantrovcmd_isr(/*core_id,*/ &hantrovcmd_data[core_id]); + return 0; + break; + } + default: + { + break; + } + } + return 0; +} +/**********************************************************************************************************\ +*process manager object management +\***********************************************************************************************************/ +#if PROCESS_MANAGER +static struct process_manager_obj *create_process_manager_obj(void) +{ + struct process_manager_obj *process_manager_obj = NULL; + process_manager_obj = vmalloc(sizeof(struct process_manager_obj)); + if (process_manager_obj == NULL) + { + PDEBUG("%s\n", "vmalloc for process_manager_obj fail!"); + return process_manager_obj; + } + memset(process_manager_obj, 0, sizeof(struct process_manager_obj)); + return process_manager_obj; +} + +static void free_process_manager_obj(struct process_manager_obj *process_manager_obj) +{ + if (process_manager_obj == NULL) + { + PDEBUG("%s\n", "free_process_manager_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(process_manager_obj); + return; +} + +static bi_list_node *create_process_manager_node(void) +{ + bi_list_node *current_node = NULL; + struct process_manager_obj *process_manager_obj = NULL; + + process_manager_obj = create_process_manager_obj(); + if (process_manager_obj == NULL) + { + PDEBUG("%s\n", "create_process_manager_obj fail!"); + return NULL; + } + process_manager_obj->total_exe_time = 0; + spin_lock_init(&process_manager_obj->spinlock); + //init_waitqueue_head(&process_manager_obj->wait_queue); + sem_init(&process_manager_obj->wait_queue, 0, ???); + current_node = bi_list_create_node(); + if (current_node == NULL) + { + PDEBUG("%s\n", "bi_list_create_node fail!"); + free_process_manager_obj(process_manager_obj); + return NULL; + } + current_node->data = (void *)process_manager_obj; + return current_node; +} +static void free_process_manager_node(bi_list_node *process_node) +{ + struct process_manager_obj *process_manager_obj = NULL; + if (process_node == NULL) + { + PDEBUG("%s\n", "free_process_manager_node NULL"); + return; + } + process_manager_obj = (struct process_manager_obj *)process_node->data; + //free struct process_manager_obj + free_process_manager_obj(process_manager_obj); + //free current process_manager_obj entity. + bi_list_free_node(process_node); + return; +} + +static long release_process_node_cleanup(bi_list *list) +{ + bi_list_node *new_process_node = NULL; + + while (1) + { + new_process_node = list->head; + if (new_process_node == NULL) + break; + //remove node from list + bi_list_remove_node(list, new_process_node); + //remove node from list + free_process_manager_node(new_process_node); + } + return 0; +} + +static void create_kernel_process_manager(void) +{ + bi_list_node *process_manager_node; + struct process_manager_obj *process_manager_obj = NULL; + process_manager_node = create_process_manager_node(); + process_manager_obj = (struct process_manager_obj *)process_manager_node->data; + //process_manager_obj->filp = NULL; + process_manager_obj->filp = 0; + bi_list_insert_node_tail(&global_process_manager, process_manager_node); +} +#endif + +int hantrovcmd_open(int *inode, int filp) +{ + int result = 0; + struct hantrovcmd_dev *dev = hantrovcmd_data; +#if PROCESS_MANAGER + bi_list_node *process_manager_node; + unsigned long flags; + struct process_manager_obj *process_manager_obj = NULL; + + //filp->private_data = (void *) dev; + process_manager_node = create_process_manager_node(); + if (process_manager_node == NULL) + return -1; + process_manager_obj = (struct process_manager_obj *)process_manager_node->data; + process_manager_obj->filp = filp; + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + bi_list_insert_node_tail(&global_process_manager, process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); +#endif + PDEBUG("dev opened, filp 0x%x\n", filp); + return result; +} +int hantrovcmd_release(/*struct inode*/ int *inode, /*struct file */ int filp) +{ + struct hantrovcmd_dev *dev; // = hantrovcmd_data; //(struct hantrovcmd_dev *) filp->private_data; + u32 core_id = 0; + u32 release_cmdbuf_num = 0; + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj_temp = NULL; +#if PROCESS_MANAGER + bi_list_node *process_manager_node; + struct process_manager_obj *process_manager_obj = NULL; +#endif + unsigned long flags; + long retVal = 0; + + PDEBUG("dev closed, filp 0x%x\n", filp); +// printf("."); + + for (core_id = 0; core_id < total_vcmd_core_num; core_id++) + { + dev = &hantrovcmd_data[core_id]; + if (dev->hwregs == NULL) + continue; + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type])) + return -ERESTARTSYS; + spin_lock_irqsave(dev->spinlock, flags); + new_cmdbuf_node = dev->list_manager.head; + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj_temp->filp == filp) + { + if (cmdbuf_obj_temp->cmdbuf_run_done) + { + cmdbuf_obj_temp->cmdbuf_need_remove = 1; + retVal = release_cmdbuf_node(&dev->list_manager, new_cmdbuf_node); +#if PROCESS_MANAGER + if (retVal == 1) + cmdbuf_obj_temp->process_manager_obj = NULL; +#endif + } + else if (cmdbuf_obj_temp->cmdbuf_data_linked == 0) + { + cmdbuf_obj_temp->cmdbuf_data_linked = 1; + cmdbuf_obj_temp->cmdbuf_run_done = 1; + cmdbuf_obj_temp->cmdbuf_need_remove = 1; + retVal = release_cmdbuf_node(&dev->list_manager, new_cmdbuf_node); +#if PROCESS_MANAGER + if (retVal == 1) + cmdbuf_obj_temp->process_manager_obj = NULL; +#endif + } + else if (cmdbuf_obj_temp->cmdbuf_data_linked == 1 && dev->working_state == WORKING_STATE_IDLE) + { + cmdbuf_obj_temp->cmdbuf_run_done = 1; + cmdbuf_obj_temp->cmdbuf_need_remove = 1; + retVal = release_cmdbuf_node(&dev->list_manager, new_cmdbuf_node); +#if PROCESS_MANAGER + if (retVal == 1) + cmdbuf_obj_temp->process_manager_obj = NULL; +#endif + } + else if (cmdbuf_obj_temp->cmdbuf_data_linked == 1 && dev->working_state == WORKING_STATE_WORKING) + { + bi_list_node *last_cmdbuf_node; + u32 record_last_cmdbuf_rdy_num; + //abort the vcmd and wait + vcmd_write_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 0); +#if 1 +#if 1 + while(1) + { + if (cmdbuf_obj_temp->cmdbuf_run_done) + break; + sched_yield(); + } +#else + queue_vcmd_wait(&abort_queue_vcmd[cmdbuf_obj_temp->cmdbuf_id]); +#endif +#else + if (wait_event_interruptible(*dev->wait_abort_queue, wait_abort_rdy(&dev[core_id]))) + return -ERESTARTSYS; +#endif + + cmdbuf_obj_temp->cmdbuf_run_done = 1; + cmdbuf_obj_temp->cmdbuf_need_remove = 1; + //printf("hantrovcmd_release before: vcmd_cmdbuf_memory_wait sem value %d\n", vcmd_cmdbuf_memory_wait.value); + retVal = release_cmdbuf_node(&dev->list_manager, new_cmdbuf_node); +#if PROCESS_MANAGER + if (retVal == 1) + cmdbuf_obj_temp->process_manager_obj = NULL; +#endif + //link + last_cmdbuf_node = find_last_linked_cmdbuf(dev->list_manager.tail); + record_last_cmdbuf_rdy_num = dev->sw_cmdbuf_rdy_num; + vcmd_link_cmdbuf(dev, last_cmdbuf_node); + //re-run + if (dev->sw_cmdbuf_rdy_num) + vcmd_start(dev, last_cmdbuf_node); + } + release_cmdbuf_num++; + } + new_cmdbuf_node = new_cmdbuf_node->next; + } + spin_unlock_irqrestore(dev->spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type]); + } +#if 0 + if(release_cmdbuf_num) + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); +#endif +#if PROCESS_MANAGER + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while (1) + { + if (process_manager_node == NULL) + break; + process_manager_obj = (struct process_manager_obj *)process_manager_node->data; + if (process_manager_obj->filp == filp) + break; + process_manager_node = process_manager_node->next; + } + //remove node from list + PDEBUG("process node %p for filp to be removed: %p\n", (void *)process_manager_node, (void *)process_manager_obj->filp); + bi_list_remove_node(&global_process_manager, process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + free_process_manager_node(process_manager_node); +#endif + return 0; +} + + +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev, bi_list_node *last_linked_cmdbuf_node) +{ + bi_list_node *new_cmdbuf_node = NULL; + bi_list_node *next_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + struct cmdbuf_obj *next_cmdbuf_obj = NULL; + u32 *jmp_addr = NULL; + u32 operation_code = 0; + new_cmdbuf_node = last_linked_cmdbuf_node; + //for the first cmdbuf. + if (new_cmdbuf_node != NULL) + { + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_data_linked == 0) + { + dev->sw_cmdbuf_rdy_num++; + cmdbuf_obj->cmdbuf_data_linked = 1; + dev->duration_without_int = 0; + if (cmdbuf_obj->has_end_cmdbuf == 0) + { + if (cmdbuf_obj->no_normal_int_cmdbuf == 1) + { + dev->duration_without_int = cmdbuf_obj->executing_time; + //maybe nop is modified, so write back. + if (dev->duration_without_int >= INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size / 4); + operation_code = *(jmp_addr - 4); + operation_code = JMP_IE_1 | operation_code; + *(jmp_addr - 4) = operation_code; + dev->duration_without_int = 0; + } + } + } + } + } + while (1) + { + if (new_cmdbuf_node == NULL) + break; + if (new_cmdbuf_node->next == NULL) + break; + next_cmdbuf_node = new_cmdbuf_node->next; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + next_cmdbuf_obj = (struct cmdbuf_obj *)next_cmdbuf_node->data; + if (cmdbuf_obj->has_end_cmdbuf == 0) + { + //need to link, current cmdbuf link to next cmdbuf + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size / 4); + if (dev->hw_version_id > HW_ID_1_0_C) + { + //set next cmdbuf id + *(jmp_addr - 1) = next_cmdbuf_obj->cmdbuf_id; + } + if (sizeof(ptr_t) == 8) + { + *(jmp_addr - 2) = (u32)((u64)(next_cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) >> 32); + } + else + { + *(jmp_addr - 2) = 0; + } + *(jmp_addr - 3) = (u32)(next_cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr); + operation_code = *(jmp_addr - 4); + operation_code >>= 16; + operation_code <<= 16; + *(jmp_addr - 4) = (u32)(operation_code | JMP_RDY_1 | ((next_cmdbuf_obj->cmdbuf_size + 7) / 8)); + next_cmdbuf_obj->cmdbuf_data_linked = 1; + dev->sw_cmdbuf_rdy_num++; + //modify nop code of next cmdbuf + if (next_cmdbuf_obj->has_end_cmdbuf == 0) + { + if (next_cmdbuf_obj->no_normal_int_cmdbuf == 1) + { + dev->duration_without_int += next_cmdbuf_obj->executing_time; + + //maybe we see the modified nop before abort, so need to write back. + if (dev->duration_without_int >= INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = next_cmdbuf_obj->cmdbuf_virtualAddress + (next_cmdbuf_obj->cmdbuf_size / 4); + operation_code = *(jmp_addr - 4); + operation_code = JMP_IE_1 | operation_code; + *(jmp_addr - 4) = operation_code; + dev->duration_without_int = 0; + } + } + } + else + { + dev->duration_without_int = 0; + } +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + printk(KERN_INFO "vcmd link, last cmdbuf content\n"); + for (i = cmdbuf_obj->cmdbuf_size / 4 - 8; i < cmdbuf_obj->cmdbuf_size / 4; i++) + { + printk(KERN_INFO "current linked cmdbuf data %d =0x%x\n", i, *(cmdbuf_obj->cmdbuf_virtualAddress + i)); + } + } +#endif + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + return; +} + +/*------------------------------------------------------------------------------ + Function name : vcmd_pcie_init + Description : Initialize PCI Hw access + + Return type : int + ------------------------------------------------------------------------------*/ +static int vcmd_pcie_init(void) +{ + int i = 0; + + g_vcmd_dev = pci_get_device(PCI_VENDOR_ID_HANTRO, PCI_DEVICE_ID_HANTRO_PCI, g_vcmd_dev); + //if (NULL == g_vcmd_dev) { + // printk(KERN_ERR "Init: Hardware not found.\n"); + // goto out; + //} + + if (0 > pci_enable_device(g_vcmd_dev)) + { + printk(KERN_ERR "Init: Device not enabled.\n"); + goto out; + } + + g_vcmd_base_hdwr = pci_resource_start(g_vcmd_dev, PCI_H2_BAR); + //if (0 > g_vcmd_base_hdwr) { + // printk(KERN_INFO "Init: Base Address not set.\n"); + // goto out_pci_disable_device; + //} + printk(KERN_INFO "Base hw val 0x%llx\n", (long long unsigned int)g_vcmd_base_hdwr); + + g_vcmd_base_len = pci_resource_len(g_vcmd_dev, PCI_H2_BAR); + printk(KERN_INFO "Base hw len 0x%d\n", (unsigned int)g_vcmd_base_len); + + for (i = 0; i < total_vcmd_core_num; i++) + { + vcmd_core_array[i].vcmd_base_addr = g_vcmd_base_hdwr + vcmd_core_array[i].vcmd_base_addr; //the offset is based on which bus interface is chosen + } + + vcmd_sram_base = g_vcmd_base_hdwr + 0x000000; //axi0 interface + vcmd_sram_size = 0x80000; + + g_vcmd_base_ddr_hw = pci_resource_start(g_vcmd_dev, PCI_DDR_BAR); + //if (0 > g_vcmd_base_ddr_hw) { + // printk(KERN_INFO "PcieInit: Base Address not set.\n"); + // goto out_pci_disable_device; + //} + printk(KERN_INFO "Base memory val 0x%08x\n", (unsigned int)g_vcmd_base_ddr_hw); + base_ddr_addr = g_vcmd_base_ddr_hw; + g_vcmd_base_len = pci_resource_len(g_vcmd_dev, PCI_DDR_BAR); + printk(KERN_INFO "Base memory len 0x%d\n", (unsigned int)g_vcmd_base_len); + + //Get bus addr for VCMD devide + vcmd_buf_mem_pool.busAddress = GetBusAddrForIODevide(CMDBUF_POOL_TOTAL_SIZE * 2 + 0x100000); //5M //g_vcmd_base_ddr_hw+0x800000; + vcmd_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + if (!request_mem_region(vcmd_buf_mem_pool.busAddress, vcmd_buf_mem_pool.size, + "vc8000_vcmd_driver")) + { + printk(KERN_INFO "Init: failed to request hw region.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_buf_mem_pool.busAddress=0x%llx.\n", (long long unsigned int)vcmd_buf_mem_pool.busAddress); + + vcmd_buf_mem_pool.virtualAddress = (u32 *)DirectMemoryMap(vcmd_buf_mem_pool.busAddress, vcmd_buf_mem_pool.size); + + if (vcmd_buf_mem_pool.virtualAddress == NULL) + { + printk(KERN_INFO "Init: failed to ioremap.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_buf_mem_pool.virtualAddress=0x%llx.\n", (long long unsigned int)vcmd_buf_mem_pool.virtualAddress); + + vcmd_status_buf_mem_pool.busAddress = vcmd_buf_mem_pool.busAddress + CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + if (!request_mem_region(vcmd_status_buf_mem_pool.busAddress, vcmd_status_buf_mem_pool.size, + "vc8000_vcmd_driver")) + { + printk(KERN_INFO "Init: failed to request hw region.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_status_buf_mem_pool.busAddress=0x%llx.\n", (long long unsigned int)vcmd_status_buf_mem_pool.busAddress); + vcmd_status_buf_mem_pool.virtualAddress = (u32 *)DirectMemoryMap(vcmd_status_buf_mem_pool.busAddress, + vcmd_status_buf_mem_pool.size); + + if (vcmd_status_buf_mem_pool.virtualAddress == NULL) + { + printk(KERN_INFO "Init: failed to ioremap.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_status_buf_mem_pool.virtualAddress=0x%llx.\n", (long long unsigned int)vcmd_status_buf_mem_pool.virtualAddress); + + vcmd_registers_mem_pool.busAddress = vcmd_status_buf_mem_pool.busAddress + CMDBUF_POOL_TOTAL_SIZE; + vcmd_registers_mem_pool.size = CMDBUF_VCMD_REGISTER_TOTAL_SIZE; + if (!request_mem_region(vcmd_registers_mem_pool.busAddress, vcmd_registers_mem_pool.size, + "vc8000_vcmd_driver")) + { + printk(KERN_INFO "Init: failed to request hw region.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_registers_mem_pool.busAddress=0x%llx.\n", (long long unsigned int)vcmd_registers_mem_pool.busAddress); + vcmd_registers_mem_pool.virtualAddress = (u32 *)DirectMemoryMap(vcmd_registers_mem_pool.busAddress, + vcmd_registers_mem_pool.size); + + if (vcmd_registers_mem_pool.virtualAddress == NULL) + { + printk(KERN_INFO "Init: failed to ioremap.\n"); + return -1; + } + printk(KERN_INFO "Init: vcmd_registers_mem_pool.virtualAddress=0x%llx.\n", (long long unsigned int)vcmd_registers_mem_pool.virtualAddress); + + return 0; + +out_pci_disable_device: + pci_disable_device(g_vcmd_dev); +out: + return -1; +} + +static void vcmd_delink_cmdbuf(struct hantrovcmd_dev *dev, bi_list_node *last_linked_cmdbuf_node) +{ + bi_list_node *new_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + + new_cmdbuf_node = last_linked_cmdbuf_node; + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_data_linked) + { + cmdbuf_obj->cmdbuf_data_linked = 0; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + dev->sw_cmdbuf_rdy_num = 0; +} + +static void vcmd_start(struct hantrovcmd_dev *dev, bi_list_node *first_linked_cmdbuf_node) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + + if (dev->working_state == WORKING_STATE_IDLE) + { + if ((first_linked_cmdbuf_node != NULL) && dev->sw_cmdbuf_rdy_num) + { + cmdbuf_obj = (struct cmdbuf_obj *)first_linked_cmdbuf_node->data; + //0x40 + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_AXI_CLK_GATE_DISABLE, 0); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE, 1); //this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_CORE_CLK_GATE_DISABLE, 0); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_ABORT_MODE, 0); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_RESET_CORE, 0); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_RESET_ALL, 0); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 0); + //0x48 + if (dev->hw_version_id <= HW_ID_1_0_C) + { + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_INTCMD_EN, 0xffff); + } + else + { + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_JMPP_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_JMPD_EN, 1); + } + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_RESET_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_ABORT_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_CMDERR_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_TIMEOUT_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_BUSERR_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_ENDCMD_EN, 1); + //0x4c + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_TIMEOUT_EN, 1); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_TIMEOUT_CYCLES, 0x1dcd6500); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR, (u32)(cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr)); + if (sizeof(ptr_t) == 8) + { + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, (u32)((u64)(cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) >> 32)); + } + else + { + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_EXE_CMDBUF_LENGTH, (u32)((cmdbuf_obj->cmdbuf_size + 7) / 8)); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_RDY_CMDBUF_COUNT, dev->sw_cmdbuf_rdy_num); + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_MAX_BURST_LEN, 0x10); + if (dev->hw_version_id > HW_ID_1_0_C) + { + vcmd_write_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_CMDBUF_EXECUTING_ID, (u32)cmdbuf_obj->cmdbuf_id); + } + vcmd_write_reg((const void *)dev->hwregs, 0x40, dev->reg_mirror[0x40 / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x44, vcmd_read_reg((const void *)dev->hwregs, 0x44)); + vcmd_write_reg((const void *)dev->hwregs, 0x48, dev->reg_mirror[0x48 / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x4c, dev->reg_mirror[0x4c / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x50, dev->reg_mirror[0x50 / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x54, dev->reg_mirror[0x54 / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x58, dev->reg_mirror[0x58 / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x5c, dev->reg_mirror[0x5c / 4]); + vcmd_write_reg((const void *)dev->hwregs, 0x60, dev->reg_mirror[0x60 / 4]); +#if 1 + vcmd_write_reg((const void *)dev->hwregs, 0x64, 0xffffffff); //not interrupt cpu +#else + // Top 20 needs to mask abnormal interrupt + vcmd_write_reg((const void *)dev->hwregs, 0x64, 0xffff0000); +#endif + + dev->working_state = WORKING_STATE_WORKING; + //start + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE, 0); //this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror, HWIF_VCMD_START_TRIGGER, 1); + vcmd_write_reg((const void *)dev->hwregs, 0x40, dev->reg_mirror[0x40 / 4]); + } + } +} + +static void create_read_all_registers_cmdbuf(struct exchange_parameter *input_para) +{ + u32 register_range[] = {VCMD_ENCODER_REGISTER_SIZE, + VCMD_IM_REGISTER_SIZE, + VCMD_DECODER_REGISTER_SIZE, + VCMD_JPEG_ENCODER_REGISTER_SIZE, + VCMD_JPEG_DECODER_REGISTER_SIZE}; + u32 counter_cmdbuf_size = 0; + u32 *set_base_addr = vcmd_buf_mem_pool.virtualAddress + input_para->cmdbuf_id * CMDBUF_MAX_SIZE / 4; + //u32 *status_base_virt_addr=vcmd_status_buf_mem_pool.virtualAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE/4+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2/4+0); + ptr_t status_base_phy_addr = vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id * CMDBUF_MAX_SIZE + (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr / 2 + 0); + u32 offset_inc = 0; + u32 offset_inc_dec400 = 0; + if (vcmd_manager[input_para->module_type][0]->hw_version_id > HW_ID_1_0_C) + { + printk(KERN_INFO "vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n", vcmd_manager[input_para->module_type][0]->hw_version_id); + + //read vcmd executing cmdbuf id registers to ddr for balancing core load. + *(set_base_addr + 0) = (OPCODE_RREG) | (1 << 16) | (EXECUTING_CMDBUF_ID_ADDR * 4); + counter_cmdbuf_size += 4; + *(set_base_addr + 1) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr + 2) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 3) = 0; + counter_cmdbuf_size += 4; + + //read main IP all registers + *(set_base_addr + 4) = (OPCODE_RREG) | ((register_range[input_para->module_type] / 4) << 16) | (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr + 0); + counter_cmdbuf_size += 4; + *(set_base_addr + 5) = (u32)(status_base_phy_addr - base_ddr_addr); + counter_cmdbuf_size += 4; + if (sizeof(ptr_t) == 8) + { + *(set_base_addr + 6) = (u32)((u64)(status_base_phy_addr - base_ddr_addr) >> 32); + } + else + { + *(set_base_addr + 6) = 0; + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 7) = 0; + counter_cmdbuf_size += 4; + + if (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr != 0xffff) + { + //read L2 cache register + offset_inc = 4; + status_base_phy_addr = vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id * CMDBUF_MAX_SIZE + (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr / 2 + 0); + //read L2cache IP first register + *(set_base_addr + 8) = (OPCODE_RREG) | (1 << 16) | (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr + 0); + counter_cmdbuf_size += 4; + *(set_base_addr + 9) = (u32)(status_base_phy_addr - base_ddr_addr); + counter_cmdbuf_size += 4; + if (sizeof(ptr_t) == 8) + { + *(set_base_addr + 10) = (u32)((u64)(status_base_phy_addr - base_ddr_addr) >> 32); + } + else + { + *(set_base_addr + 10) = 0; + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 11) = 0; + counter_cmdbuf_size += 4; + } +#if 0 + if (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr != 0xffff) + { + //read dec400 register + offset_inc_dec400 = 4; + status_base_phy_addr = vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id * CMDBUF_MAX_SIZE + (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr / 2 + 0); + //read L2cache IP first register + *(set_base_addr + 8 + offset_inc) = (OPCODE_RREG) | (0x2b << 16) | (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr + 0); + counter_cmdbuf_size += 4; + *(set_base_addr + 9 + offset_inc) = (u32)(status_base_phy_addr - base_ddr_addr); + counter_cmdbuf_size += 4; + if (sizeof(ptr_t) == 8) + { + *(set_base_addr + 10 + offset_inc) = (u32)((u64)(status_base_phy_addr - base_ddr_addr) >> 32); + } + else + { + *(set_base_addr + 10 + offset_inc) = 0; + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 11 + offset_inc) = 0; + counter_cmdbuf_size += 4; + } +#endif +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //read vcmd registers to ddr + *(set_base_addr + 8 + offset_inc + offset_inc_dec400) = (OPCODE_RREG) | (27 << 16) | (0); + counter_cmdbuf_size += 4; + *(set_base_addr + 9 + offset_inc + offset_inc_dec400) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr + 10 + offset_inc + offset_inc_dec400) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 11 + offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + //JMP RDY = 0 + *(set_base_addr + 12 + offset_inc + offset_inc_dec400) = (OPCODE_JMP_RDY0) | 0 | JMP_IE_1 | 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 13 + offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 14 + offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 15 + offset_inc + offset_inc_dec400) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size = (16 + offset_inc + offset_inc_dec400) * 4; + } + else + { + printk(KERN_INFO "vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n", vcmd_manager[input_para->module_type][0]->hw_version_id); + //read all registers + *(set_base_addr + 0) = (OPCODE_RREG) | ((register_range[input_para->module_type] / 4) << 16) | (vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr + 0); + counter_cmdbuf_size += 4; + *(set_base_addr + 1) = (u32)(status_base_phy_addr - base_ddr_addr); + counter_cmdbuf_size += 4; + if (sizeof(ptr_t) == 8) + { + *(set_base_addr + 2) = (u32)((u64)(status_base_phy_addr - base_ddr_addr) >> 32); + } + else + { + *(set_base_addr + 2) = 0; + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr + 3) = 0; + counter_cmdbuf_size += 4; +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //JMP RDY = 0 + *(set_base_addr + 4) = (OPCODE_JMP_RDY0) | 0 | JMP_IE_1 | 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 5) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 6) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr + 7) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size = 8 * 4; + } +} +volatile int tmpTimer=100000; +static void read_main_module_all_registers(u32 main_module_type) +{ + int ret; + struct exchange_parameter input_para; + u32 irq_status_ret = 0; + u32 *status_base_virt_addr; + + input_para.executing_time = 0; + input_para.priority = CMDBUF_PRIORITY_NORMAL; + input_para.module_type = main_module_type; + input_para.cmdbuf_size = 0; + //ret = reserve_cmdbuf(NULL,&input_para); + ret = reserve_cmdbuf(0, &input_para); + vcmd_manager[main_module_type][0]->status_cmdbuf_id = input_para.cmdbuf_id; + create_read_all_registers_cmdbuf(&input_para); + //link_and_run_cmdbuf(NULL,&input_para); + link_and_run_cmdbuf(0, &input_para); +#if 0 + msleep(1000); +#else + while(tmpTimer--); +#endif + hantrovcmd_isr(/*input_para.core_id,*/ &hantrovcmd_data[input_para.core_id]); + //wait_cmdbuf_ready(NULL,input_para.cmdbuf_id,&irq_status_ret); + wait_cmdbuf_ready(0, input_para.cmdbuf_id, &irq_status_ret); + status_base_virt_addr = vcmd_status_buf_mem_pool.virtualAddress + input_para.cmdbuf_id * CMDBUF_MAX_SIZE / 4 + (vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr / 2 / 4 + 0); + printf(KERN_INFO "vc8000_vcmd_driver: main module register 0:0x%x\n", *status_base_virt_addr); + printf(KERN_INFO "vc8000_vcmd_driver: main module register 50:0x%08x\n", *(status_base_virt_addr + 50)); + printf(KERN_INFO "vc8000_vcmd_driver: main module register 54:0x%08x\n", *(status_base_virt_addr + 54)); + printf(KERN_INFO "vc8000_vcmd_driver: main module register 56:0x%08x\n", *(status_base_virt_addr + 56)); + printf(KERN_INFO "vc8000_vcmd_driver: main module register 309:0x%08x\n", *(status_base_virt_addr + 309)); + //don't release cmdbuf because it can be used repeatedly + //release_cmdbuf(NULL, input_para.cmdbuf_id); + //release_cmdbuf(0,input_para.cmdbuf_id); +} + +int __init hantrovcmd_init(void) +{ + int i = 0, k = 0; + int result = 0, irq_enabled = 0; + + total_vcmd_core_num = sizeof(vcmd_core_array) / sizeof(struct vcmd_config); + result = vcmd_pcie_init(); + if (result) + goto err1; + + for (i = 0; i < total_vcmd_core_num; i++) + { + printk(KERN_INFO "vcmd: module init - vcmdcore[%d] addr =0x%llx\n", i, + (long long unsigned int)vcmd_core_array[i].vcmd_base_addr); + } + hantrovcmd_data = (struct hantrovcmd_dev *)vmalloc(sizeof(struct hantrovcmd_dev) * total_vcmd_core_num); + if (hantrovcmd_data == NULL) + goto err1; + memset(hantrovcmd_data, 0, sizeof(struct hantrovcmd_dev) * total_vcmd_core_num); + for (k = 0; k < MAX_VCMD_TYPE; k++) + { + vcmd_type_core_num[k] = 0; + vcmd_position[k] = 0; + for (i = 0; i < MAX_VCMD_NUMBER; i++) + { + vcmd_manager[k][i] = NULL; + } + } +#if PROCESS_MANAGER + init_bi_list(&global_process_manager); +#endif + for (i = 0; i < total_vcmd_core_num; i++) + { + hantrovcmd_data[i].vcmd_core_cfg = vcmd_core_array[i]; + hantrovcmd_data[i].hwregs = NULL; + hantrovcmd_data[i].core_id = i; + hantrovcmd_data[i].working_state = WORKING_STATE_IDLE; + hantrovcmd_data[i].sw_cmdbuf_rdy_num = 0; + hantrovcmd_data[i].spinlock = &owner_lock_vcmd[i]; + spin_lock_init(&owner_lock_vcmd[i]); +#if 1 +// hantrovcmd_data[i].wait_queue = &wait_queue_vcmd[i]; +//wait_queue_vcmd[i] = xSemaphoreCreateCounting(0x7FFU, 0); +// sem_init(&wait_queue_vcmd[i], 0, 0); +// hantrovcmd_data[i].wait_abort_queue=&abort_queue_vcmd[i]; +//abort_queue_vcmd[i] = xSemaphoreCreateCounting(0x7FFU, 0); +// sem_init(&abort_queue_vcmd[i], 0, 0); +#else + hantrovcmd_data[i].wait_queue = &wait_queue_vcmd[i]; + init_waitqueue_head(&wait_queue_vcmd[i]); + hantrovcmd_data[i].wait_abort_queue = &abort_queue_vcmd[i]; + init_waitqueue_head(&abort_queue_vcmd[i]); +#endif + init_bi_list(&hantrovcmd_data[i].list_manager); + hantrovcmd_data[i].duration_without_int = 0; + vcmd_manager[vcmd_core_array[i].sub_module_type][vcmd_type_core_num[vcmd_core_array[i].sub_module_type]] = &hantrovcmd_data[i]; + vcmd_type_core_num[vcmd_core_array[i].sub_module_type]++; + hantrovcmd_data[i].vcmd_reg_mem_busAddress = vcmd_registers_mem_pool.busAddress + i * VCMD_REGISTER_SIZE - base_ddr_addr; + hantrovcmd_data[i].vcmd_reg_mem_virtualAddress = vcmd_registers_mem_pool.virtualAddress + i * VCMD_REGISTER_SIZE / 4; + hantrovcmd_data[i].vcmd_reg_mem_size = VCMD_REGISTER_SIZE; + memset(hantrovcmd_data[i].vcmd_reg_mem_virtualAddress, 0, VCMD_REGISTER_SIZE); + } + //init_waitqueue_head(&mc_wait_queue); + //sema_init(&mc_wait_queue, 0); + queue_vcmd_init(&mc_wait_queue); + for (i = 0; i < TOTAL_DISCRETE_CMDBUF_NUM; i++) + { + queue_vcmd_init(&wait_queue_vcmd[i]); + queue_vcmd_init(&abort_queue_vcmd[i]); + } + + result = register_chrdev(hantrovcmd_major, "vc8000_vcmd_driver", &hantrovcmd_fops); + if (result < 0) + { + printk(KERN_INFO "vc8000_vcmd_driver: unable to get major <%d>\n", + hantrovcmd_major); + goto err1; + } + else if (result != 0) /* this is for dynamic major */ + { + hantrovcmd_major = result; + } + result = vcmd_reserve_IO(); + if (result < 0) + { + goto err; + } + vcmd_reset_asic(hantrovcmd_data); + + /* get the IRQ line */ + for (i = 0; i < total_vcmd_core_num; i++) + { + if (hantrovcmd_data[i].hwregs == NULL) + continue; + if (hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq != -1) + { + //cpu interrrupt enable + IntEnableIRQ(hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq); + irq_enabled = 1; + } + else + { + printk(KERN_INFO "vc8000_vcmd_driver: IRQ not in use!\n"); + } + } + if (irq_enabled == 1) + { + result = request_irq(CPU_INT_IRQ, hantrovcmd_isr, + IRQF_SHARED, + "vc8000_vcmd_driver", (void *)&hantrovcmd_data[i]); + + if (result == -EINVAL) + { + printk(KERN_ERR "vc8000_vcmd_driver: Bad vcmd_irq number or handler, core_id=%d\n", i); + vcmd_release_IO(); + goto err; + } + else if (result == -EBUSY) + { + printk(KERN_ERR "vc8000_vcmd_driver: IRQ <%d> busy, change your config, core_id=%d\n", + hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq, i); + vcmd_release_IO(); + goto err; + } + } + //cmdbuf pool allocation + //init_vcmd_non_cachable_memory_allocate(); + //for cmdbuf management + cmdbuf_used_pos = 0; + for (k = 0; k < TOTAL_DISCRETE_CMDBUF_NUM; k++) + { + cmdbuf_used[k] = 0; + global_cmdbuf_node[k] = NULL; + } + //cmdbuf_used[0] not be used, because int vector must non-zero + cmdbuf_used_residual = TOTAL_DISCRETE_CMDBUF_NUM; + cmdbuf_used_pos = 1; + cmdbuf_used[0] = 1; + cmdbuf_used_residual -= 1; + + printk(KERN_INFO "vc8000_vcmd_driver: module inserted. Major <%d>\n", hantrovcmd_major); +#if PROCESS_MANAGER + create_kernel_process_manager(); +#endif + for (i = 0; i < MAX_VCMD_TYPE; i++) + { + if (vcmd_type_core_num[i] == 0) + continue; +#if 1 + vcmd_reserve_cmdbuf_sem[i] = PTHREAD_MUTEX_INITIALIZER; +#else + sema_init(&vcmd_reserve_cmdbuf_sem[i], 1); +#endif + } + sema_init(&vcmd_cmdbuf_memory_wait, TOTAL_DISCRETE_CMDBUF_NUM - 1); + +#ifdef IRQ_SIMULATION + for (i = 0; i < 10000; i++) + { + timer_reserve[i].timer = NULL; + } +#endif + /*read all registers for each type of module for analyzing configuration in cwl*/ + vcmd_init_flag = 1; + for (i = 0; i < MAX_VCMD_TYPE; i++) + { + if (vcmd_type_core_num[i] == 0) + continue; + read_main_module_all_registers(i); + } + vcmd_init_flag = 0; + + printf("vcmd init successful...\n"); + return 0; +err: + unregister_chrdev(hantrovcmd_major, "vc8000_vcmd_driver"); +err1: + if (hantrovcmd_data != NULL) + vfree(hantrovcmd_data); + printk(KERN_INFO "vc8000_vcmd_driver: module not inserted\n"); + return result; +} + +void __exit hantrovcmd_cleanup(void) +{ + int i = 0; + u32 result; + + for (i = 0; i < total_vcmd_core_num; i++) + { + if (hantrovcmd_data[i].hwregs == NULL) + continue; + //disable interrupt at first + vcmd_write_reg((const void *)hantrovcmd_data[i].hwregs, VCMD_REGISTER_INT_CTL_OFFSET, 0x0000); + //disable HW + vcmd_write_reg((const void *)hantrovcmd_data[i].hwregs, VCMD_REGISTER_CONTROL_OFFSET, 0x0000); + //read status register + result = vcmd_read_reg((const void *)hantrovcmd_data[i].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + //clean status register + vcmd_write_reg((const void *)hantrovcmd_data[i].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, result); + + /* free the vcmd IRQ */ + if (hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq != -1) + { + free_irq(hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq, (void *)&hantrovcmd_data[i]); + } + release_cmdbuf_node_cleanup(&hantrovcmd_data[i].list_manager); + } +#if PROCESS_MANAGER + release_process_node_cleanup(&global_process_manager); +#endif + vcmd_release_IO(); + vfree(hantrovcmd_data); + + //release_vcmd_non_cachable_memory(); + iounmap((void *)vcmd_buf_mem_pool.virtualAddress); + release_mem_region(vcmd_buf_mem_pool.busAddress, vcmd_buf_mem_pool.size); + iounmap((void *)vcmd_status_buf_mem_pool.virtualAddress); + release_mem_region(vcmd_status_buf_mem_pool.busAddress, vcmd_status_buf_mem_pool.size); + iounmap((void *)vcmd_registers_mem_pool.virtualAddress); + release_mem_region(vcmd_registers_mem_pool.busAddress, vcmd_registers_mem_pool.size); + unregister_chrdev(hantrovcmd_major, "vc8000_vcmd_driver"); + + printk(KERN_INFO "vc8000_vcmd_driver: module removed\n"); + return; +} + +static int vcmd_reserve_IO(void) +{ + u32 hwid; + int i; + u32 found_hw = 0; + + for (i = 0; i < total_vcmd_core_num; i++) + { + hantrovcmd_data[i].hwregs = NULL; + + if (!request_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize, "vc8000_vcmd_driver")) + { + printk(KERN_INFO "hantrovcmd: failed to reserve HW regs\n"); + continue; + } + + hantrovcmd_data[i].hwregs = + (volatile u8 *)ioremap_nocache(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, + hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + + if (hantrovcmd_data[i].hwregs == NULL) + { + printk(KERN_INFO "hantrovcmd: failed to ioremap HW regs\n"); + release_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + continue; + } + + /*read hwid and check validness and store it*/ + hwid = (u32)ioread32((void *)hantrovcmd_data[i].hwregs); + printk(KERN_INFO "hwid=0x%08x\n", hwid); + hantrovcmd_data[i].hw_version_id = hwid; + + /* check for vcmd HW ID */ + if (((hwid >> 16) & 0xFFFF) != VCMD_HW_ID) + { + printk(KERN_INFO "hantrovcmd: HW not found at 0x%llx\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr); + iounmap((void *)hantrovcmd_data[i].hwregs); + release_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + hantrovcmd_data[i].hwregs = NULL; + continue; + } + + found_hw = 1; + + printk(KERN_INFO + "hantrovcmd: HW at base <0x%llx> with ID <0x%08x>\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hwid); + } + + if (found_hw == 0) + { + printk(KERN_ERR "hantrovcmd: NO ANY HW found!!\n"); + return -1; + } + + return 0; +} + +static void vcmd_release_IO(void) +{ + u32 i; + for (i = 0; i < total_vcmd_core_num; i++) + { + if (hantrovcmd_data[i].hwregs) + { + iounmap((void *)hantrovcmd_data[i].hwregs); + release_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + hantrovcmd_data[i].hwregs = NULL; + } + } +} + +static irqreturn_t ProcessingInterrupt(void *dev_id, unsigned int * handled) +{ + struct hantrovcmd_dev *dev = (struct hantrovcmd_dev *) dev_id; + unsigned long flags = 0; + bi_list_node *new_cmdbuf_node = NULL; + bi_list_node *base_cmdbuf_node = NULL; + struct cmdbuf_obj *cmdbuf_obj = NULL; + ptr_t /*size_t*/ exe_cmdbuf_busAddress = 0; + u32 irq_status = 0; + u32 cmdbuf_processed_num = 0; + u32 cmdbuf_id = 0; + + /*If core is not reserved by any user, but irq is received, just clean it*/ + isr_spin_lock_irqsave(dev->spinlock, flags); + if (dev->list_manager.head == NULL) + { + PDEBUG("In ISR, received IRQ but core has nothing to do.\n"); + irq_status = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, irq_status); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + irq_status = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + for (i = 0; i < ASIC_VCMD_SWREG_AMOUNT; i++) + { + fordebug = vcmd_read_reg((const void *)dev->hwregs, i * 4); + printk(KERN_INFO "vcmd register %d:0x%x\n", i, fordebug); + } + } +#endif + + if (!irq_status) + { + //printk(KERN_INFO"hantrovcmd_isr error,irq_status :0x%x",irq_status); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + + PDEBUG("In ISR, received IRQ, irq_status of %d is:%x\n", dev->core_id, irq_status); + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, irq_status); + dev->reg_mirror[VCMD_REGISTER_INT_STATUS_OFFSET / 4] = irq_status; + + if ((dev->hw_version_id > HW_ID_1_0_C) && (irq_status & 0x3f)) + { + //if error,read from register directly. + cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_CMDBUF_EXECUTING_ID); + if (cmdbuf_id >= TOTAL_DISCRETE_CMDBUF_NUM) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else if ((dev->hw_version_id > HW_ID_1_0_C)) + { + //read cmdbuf id from ddr +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + printk(KERN_INFO "ddr vcmd register phy_addr=0x%x\n", dev->vcmd_reg_mem_busAddress); + printk(KERN_INFO "ddr vcmd register virt_addr=0x%x\n", dev->vcmd_reg_mem_virtualAddress); + for (i = 0; i < ASIC_VCMD_SWREG_AMOUNT; i++) + { + fordebug = *(dev->vcmd_reg_mem_virtualAddress + i); + printk(KERN_INFO "ddr vcmd register %d:0x%x\n", i, fordebug); + } + } +#endif + + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress + EXECUTING_CMDBUF_ID_ADDR); + PDEBUG("In ISR, the current cmdbuf_id is [%d]\n", cmdbuf_id); + if (cmdbuf_id >= TOTAL_DISCRETE_CMDBUF_NUM) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_RESET)) + { + //reset error,all cmdbuf that is not done will be run again. + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + //find the first run_done=0 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + base_cmdbuf_node = new_cmdbuf_node; + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + vcmd_link_cmdbuf(dev, base_cmdbuf_node); + if (dev->sw_cmdbuf_rdy_num != 0) + { + //restart new command + vcmd_start(dev, base_cmdbuf_node); + } + (*handled)++; + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_ABORT)) + { + //abort error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if (dev->hw_version_id > HW_ID_1_0_C) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmderror cmdbuf + while (1) + { + if (new_cmdbuf_node == NULL) + { + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if ((((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) <= exe_cmdbuf_busAddress) && (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr + cmdbuf_obj->cmdbuf_size) > exe_cmdbuf_busAddress))) && (cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node = base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if 1 + //queue_vcmd_wakeup(&abort_queue_vcmd[cmdbuf_id]); + queue_vcmd_wakeup(&mc_wait_queue); +#else + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + //to let high priority cmdbuf be inserted + wake_up_interruptible_all(dev->wait_abort_queue); + wake_up_interruptible_all(&mc_wait_queue); +#endif + + (*handled)++; + return IRQ_HANDLED; + } + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_BUSERR)) + { + //bus error ,don't need to reset , where to record status? + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if (dev->hw_version_id > HW_ID_1_0_C) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the buserr cmdbuf + while (1) + { + if (new_cmdbuf_node == NULL) + { + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if ((((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) <= exe_cmdbuf_busAddress) && (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr + cmdbuf_obj->cmdbuf_size) > exe_cmdbuf_busAddress))) && (cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + //queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if (new_cmdbuf_node != NULL) + { + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_BUSERR; + } + base_cmdbuf_node = base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + vcmd_link_cmdbuf(dev, base_cmdbuf_node); + if (dev->sw_cmdbuf_rdy_num != 0) + { + //restart new command + vcmd_start(dev, base_cmdbuf_node); + } + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if PROCESS_MANAGER + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); +#endif + + (*handled)++; + //wake_up_interruptible_all(&mc_wait_queue); + queue_vcmd_wakeup(&mc_wait_queue); + return IRQ_HANDLED; + } + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_TIMEOUT)) + { + //time out,need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if (dev->hw_version_id > HW_ID_1_0_C) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the timeout cmdbuf + while (1) + { + if (new_cmdbuf_node == NULL) + { + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if ((((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) <= exe_cmdbuf_busAddress) && (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr + cmdbuf_obj->cmdbuf_size) > exe_cmdbuf_busAddress))) && (cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + //queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + vcmd_link_cmdbuf(dev, base_cmdbuf_node); + if (dev->sw_cmdbuf_rdy_num != 0) + { + //reset + vcmd_reset_current_asic(dev); + //restart new command + vcmd_start(dev, base_cmdbuf_node); + } + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if PROCESS_MANAGER + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); +#endif + + (*handled)++; + //wake_up_interruptible_all(&mc_wait_queue); + queue_vcmd_wakeup(&mc_wait_queue); + return IRQ_HANDLED; + } + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_CMDERR)) + { + //command error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if (dev->hw_version_id > HW_ID_1_0_C) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs, dev->reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmderror cmdbuf + while (1) + { + if (new_cmdbuf_node == NULL) + { + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if ((((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr) <= exe_cmdbuf_busAddress) && (((cmdbuf_obj->cmdbuf_busAddress - base_ddr_addr + cmdbuf_obj->cmdbuf_size) > exe_cmdbuf_busAddress))) && (cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + //queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if (new_cmdbuf_node != NULL) + { + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_CMDERR; //cmderr + } + base_cmdbuf_node = base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + vcmd_link_cmdbuf(dev, base_cmdbuf_node); + if (dev->sw_cmdbuf_rdy_num != 0) + { + //restart new command + vcmd_start(dev, base_cmdbuf_node); + } + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if PROCESS_MANAGER + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); +#endif + + (*handled)++; + //wake_up_interruptible_all(&mc_wait_queue); + queue_vcmd_wakeup(&mc_wait_queue); + return IRQ_HANDLED; + } + + if (vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_ENDCMD)) + { + //end command interrupt + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if (dev->hw_version_id > HW_ID_1_0_C) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + //find the end cmdbuf + while (1) + { + if (new_cmdbuf_node == NULL) + { + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if ((cmdbuf_obj->has_end_cmdbuf == 1) && (cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + //queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node = base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev, base_cmdbuf_node); + vcmd_link_cmdbuf(dev, base_cmdbuf_node); + if (dev->sw_cmdbuf_rdy_num != 0) + { + //restart new command + vcmd_start(dev, base_cmdbuf_node); + } + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if PROCESS_MANAGER + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); +#endif + + (*handled)++; + //wake_up_interruptible_all(&mc_wait_queue); + queue_vcmd_wakeup(&mc_wait_queue); + return IRQ_HANDLED; + } + if (dev->hw_version_id <= HW_ID_1_0_C) + cmdbuf_id = vcmd_get_register_mirror_value(dev->reg_mirror, HWIF_VCMD_IRQ_INTCMD); + if (cmdbuf_id) + { + if (dev->hw_version_id <= HW_ID_1_0_C) + { + if (cmdbuf_id >= TOTAL_DISCRETE_CMDBUF_NUM) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if (new_cmdbuf_node == NULL) + { + printk(KERN_ERR "hantrovcmd_isr error cmdbuf_id !!\n"); + isr_spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + // interrupt cmdbuf and cmdbufs prior to itself, run_done = 1 + while (1) + { + if (new_cmdbuf_node == NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj *)new_cmdbuf_node->data; + if (cmdbuf_obj->cmdbuf_run_done == 0) + { + cmdbuf_obj->cmdbuf_run_done = 1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + //queue_vcmd_wakeup(&wait_queue_vcmd[cmdbuf_obj->cmdbuf_id]); + PDEBUG("In ISR, post wait_queue_vcmd[%d]\n", cmdbuf_obj->cmdbuf_id); + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + (*handled)++; + } + + isr_spin_unlock_irqrestore(dev->spinlock, flags); +#if PROCESS_MANAGER + if (cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); +#endif + + return IRQ_HANDLED; +} +static irqreturn_t hantrovcmd_isr(void *dev_id) +{ + struct hantrovcmd_dev *dev = NULL; + unsigned int handled = 0; + u32 i = 0, irq = 0, intr_val = 0, core_id = 0; + if (vcmd_init_flag) + { + ProcessingInterrupt(dev_id, &handled); + for (i = 0; i < total_vcmd_core_num; i++) + IntClearIRQStatus(vcmd_core_array[i].vcmd_irq); + } + else + { + intr_val = (u32)ioread32((void *)SYS_REG_INT_STAT/*SYS_REG_INT_VAL*/); + PDEBUG("In ISR, the interrupt value is 0x%x\n", intr_val); + irq = 0; + while (intr_val) + { + if (intr_val & 1) + { + for (i = 0; i < total_vcmd_core_num; i++) + { + if (vcmd_core_array[i].vcmd_irq != irq) + { + continue; + } + else + { //found out the trigger source cor for interrupt + /* get dev & core_id */ + core_id = i; + dev = &hantrovcmd_data[core_id]; + IntClearIRQStatus(irq); + + /* interrupt processing */ + ProcessingInterrupt(dev, &handled); + break; // have found the trigger source core, exit the for(total_vcmd_core) + } + } + } + /* next irq */ + intr_val >>= 1; + irq++; + } + } + if (!handled) + { + printf("IRQ received, but not hantro's!\n"); + } + //wake_up_interruptible_all(&mc_wait_queue); + queue_vcmd_wakeup(&mc_wait_queue); +#if 0 + static int i_lxj = 0; + printf("post %d mc_wait_queue\n", ++i_lxj); +#endif + return IRQ_HANDLED; +} + +void vcmd_reset_asic(struct hantrovcmd_dev *dev) +{ + int i, n; + u32 result; + for (n = 0; n < total_vcmd_core_num; n++) + { + if (dev[n].hwregs != NULL) + { + //disable interrupt at first + vcmd_write_reg((const void *)dev[n].hwregs, VCMD_REGISTER_INT_CTL_OFFSET, 0x0000); + //reset all + vcmd_write_reg((const void *)dev[n].hwregs, VCMD_REGISTER_CONTROL_OFFSET, 0x0004); + //read status register + result = vcmd_read_reg((const void *)dev[n].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + //clean status register + vcmd_write_reg((const void *)dev[n].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, result); + for (i = VCMD_REGISTER_CONTROL_OFFSET; i < dev[n].vcmd_core_cfg.vcmd_iosize; i += 4) + { + //set all register 0 + vcmd_write_reg((const void *)dev[n].hwregs, i, 0x0000); + } + //enable all interrupt + vcmd_write_reg((const void *)dev[n].hwregs, VCMD_REGISTER_INT_CTL_OFFSET, 0xffffffff); + } + } +} + +static void vcmd_reset_current_asic(struct hantrovcmd_dev *dev) +{ + u32 result; + + if (dev->hwregs != NULL) + { + //disable interrupt at first + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_CTL_OFFSET, 0x0000); + //reset all + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_CONTROL_OFFSET, 0x0002); + //read status register + result = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + //clean status register + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, result); + } +} + +/* +* the related code for xtensa interrupt interfaces +*/ +av_unused int RegisterIRQ(i32 irq_offset, IRQHandler isr_handler, i32 flag, const char *name, void *data) +{ + //CPU interrupt handler + //_xtos_set_interrupt_handle + u32 irq_mask = 1 << irq_offset; //irq_offset -> Irq[0] is 8 + u32 enabled_int = 0; + if ((xthal_get_intenable() & irq_mask) == 0) + { + /* Clear MCPU DB2 interrupt before enable */ + xthal_set_intclear(irq_mask); + /* Assign Interrupt handler */ + xt_set_interrupt_handler(irq_offset, (xt_handler)isr_handler, data); + + xt_ints_on(irq_mask); + + enabled_int = xthal_get_intenable(); + PDEBUG("INTs are enabled: 0x%x\n", enabled_int); + } + + return 0; +} + +static void IntEnableIRQ(u32 irq_offset) +{ + //sys reg interrupt enable + u32 irq_mask = 1 << irq_offset; + u32 curr_en; + + *((volatile uint32_t *)SYS_REG_INT_STAT) = irq_mask; // clear this irq + + curr_en = ioread32((void *)SYS_REG_INT_EN) | irq_mask; + *((volatile uint32_t *)SYS_REG_INT_EN) = curr_en; // enable this irq + + g_vc8000_int_enable_mask |= irq_mask; +} + +av_unused void IntDisableIRQ(u32 irq_offset) +{ + //sys reg interrupt disable + u32 irq_mask = 1 << irq_offset; + u32 curr_en; + + *((volatile uint32_t *)SYS_REG_INT_STAT) = irq_mask; // clear this irq + + curr_en = ioread32((void *)SYS_REG_INT_EN) & (~irq_mask); + *((volatile uint32_t *)SYS_REG_INT_EN) = curr_en; // disable this irq +} + +static void IntClearIRQStatus(u32 irq_offset) +{ + //sys reg interrupt clear + u32 irq_mask = 1 << irq_offset; + + *((volatile uint32_t *)SYS_REG_INT_STAT) = irq_mask; // clear this irq +} + +static inline uint32_t ReadInterruptStatus(void) +{ + uint32_t interrupt; + __asm__ __volatile__("rsr %0, interrupt" + : "=a"(interrupt)); + return interrupt; +} +//For debug +av_unused u32 IntGetIRQStatus(u32 irq_offset) +{ + uint32_t ret_val; + //ReadInterruptStatus + ret_val = ReadInterruptStatus(); + //mask other interrupts ? + ret_val &= (1 << irq_offset); + // return 1 if requested interrupt is set + if (ret_val) + ret_val = 1; + return 0; +} + +void queue_vcmd_init(void *semaphore) +{ +#ifdef VCMD_POLLING + sem_t *sem_temp = (sem_t *)semaphore; + sem_init(sem_temp, 0, 0); +#else + SemaphoreHandle_t *sem_temp = (SemaphoreHandle_t *)semaphore; + *sem_temp = xSemaphoreCreateBinary(); //xSemaphoreCreateCounting(0x7FFU, 0); +#endif +} + +u32 queue_vcmd_wait(void *semaphore) +{ +#ifdef VCMD_POLLING + sem_t *sem_temp = (sem_t *)semaphore; + sem_wait(sem_temp); +#else + SemaphoreHandle_t *sem_temp = (SemaphoreHandle_t *)semaphore; + xSemaphoreTake(*sem_temp, portMAX_DELAY); +#endif + return 0; +} + +void queue_vcmd_wakeup(void *semaphore) +{ +//use sched_yield to free the cpu cycles of hantrovcmd_isr functions + //u32 wake_up_start = xthal_get_ccount(); +#ifdef VCMD_POLLING + sem_t * sem_temp = (sem_t *) semaphore; + sem_post(sem_temp); +#else + SemaphoreHandle_t * sem_temp = (SemaphoreHandle_t *)semaphore; + if(vcmd_init_flag) + xSemaphoreGive(*sem_temp); + else + xSemaphoreGiveFromISR(*sem_temp, NULL); +#endif + //u32 wake_up_end = xthal_get_ccount(); + //printf("sem post cpu cycle is %d\n", wake_up_end-wake_up_start); +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.c new file mode 100644 index 000000000..c3516ea0a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.c @@ -0,0 +1,86 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include "io_tools.h" + +inline u32 ioread32(volatile void* addr) { + return *(volatile u32*)(addr); +} + +inline void iowrite32(u32 val,volatile void *addr) { + *(volatile u32*)addr = val; +} +inline u16 ioread16(volatile void* addr) { + return *(volatile u16*)(addr); +} + +inline void iowrite16(u16 val,volatile void *addr) { + *(volatile u16*)addr = val; +} + +inline u8 ioread8(volatile void* addr) { + return *(volatile u8*)(addr); +} + +inline void iowrite8(u8 val,volatile void *addr) { + *(volatile u8*)addr = val; +} + +inline u32 readl(volatile void* addr) { + return *(volatile u32*)(addr); +} + +inline void writel(unsigned int v, volatile void *addr) { + *(volatile unsigned int *)addr = /*cpu_to_le32*/(v); +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.h new file mode 100644 index 000000000..1f38f5f5b --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/io_tools.h @@ -0,0 +1,89 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _IO_TOOLS_ +#define _IO_TOOLS_ + +#include "basetype.h" + +u32 ioread32(volatile void* addr); +void iowrite32(u32 val,volatile void *addr); +u16 ioread16(volatile void* addr); +u8 ioread8(volatile void* addr); +void iowrite16(u16 val,volatile void *addr); +void iowrite8(u8 val,volatile void *addr); +u32 readl(volatile void* addr); +void writel(unsigned int v, volatile void *addr); +#define read_mreg32(addr) ioread32((void*)(addr)) +#define write_mreg32(addr,val) iowrite32(val, (void*)(addr)) +#define read_mreg16(addr) ioread16((void*)addr) +#define write_mreg16(addr,val) iowrite16(val, (void*)(addr)) +#define read_mreg8(addr) ioread8((void*)addr) +#define write_mreg8(addr,val) iowrite8(val, (void*)(addr)) + +//Dec +int hantrodec_init(void); +int hantrodec_open(int *inode, int filp); +int hantrodec_release(int *inode, int filp); +long hantrodec_ioctl(int filp, unsigned int cmd, void *arg); + +//VCMD interfaces funcs +int hantrovcmd_init(void); +int hantrovcmd_open(int *inode, int filp); +int hantrovcmd_release(int *inode, int filp); +long hantrovcmd_ioctl(int filp, unsigned int cmd, void *arg); +//typedef int (*IRQHandler)(i32 i, void* data); +typedef void (*IRQHandler)(void* data); + +#endif //_IO_TOOLS_ \ No newline at end of file diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/memalloc_freertos.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/memalloc_freertos.c new file mode 100644 index 000000000..c04b886f2 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/memalloc_freertos.c @@ -0,0 +1,484 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#include "osal.h" +#include "memalloc_freertos.h" + +/* Dev Memory Map for NETNT */ +#define DEV_MEM_BANK_0 (0) +#define MEM_BANK0_SIZE (16*1024*1024*1024LL) +#define DEV_MEM_BANK_1 (1) +#define MEM_BANK1_SIZE (16*1024*1024*1024LL) +#if 1 +#define SHARED_MEM_DEV_ADDRESS (0x840000000LL) //TOP 29 +#define SHARED_MEM_CPU_ADDRESS (0x40000000) +//#define SHARED_MEM_CPU_ADDRESS (0x30000000) +//#define SHARED_MEM_CPU_ADDRESS (0x30000000) +#define SHARED_MEM_SIZE (2*1024*1024*1024LL) +#define SHARED_MEM_BANK (DEV_MEM_BANK_0) +#else +#define SHARED_MEM_DEV_ADDRESS (0x800000000LL) //TOP24 +#define SHARED_MEM_CPU_ADDRESS (0x80000000) +#define SHARED_MEM_SIZE (2*1024*1024*1024LL) +#define SHARED_MEM_BANK (DEV_MEM_BANK_0) +#endif + +#define MEM_0_DEV_ADDRESS (SHARED_MEM_DEV_ADDRESS + SHARED_MEM_SIZE) +#define MEM_0_CPU_ADDRESS (0) +#define MEM_0_SIZE (MEM_BANK0_SIZE + MEM_BANK1_SIZE - SHARED_MEM_SIZE) +#define MEM_0_BANK (DEV_MEM_BANK_0) + +/* +#define MEM_1_DEV_ADDRESS (0xC00000000LL) +#define MEM_1_CPU_ADDRESS (0) +#define MEM_1_SIZE (16*1024*1024*1024LL) +#define MEM_1_BANK (DEV_MEM_BANK_1) +*/ + +/* the size of chunk in MEMALLOC_DYNAMIC */ +#define CHUNK_SIZE (16*1024) + +/* DO NOT change below defines, they need to be matched with DWL_MEM_TYPE_xx */ +#define _MEM_TYPE_CPU 0U /* CPU RW. non-secure CMA memory */ +#define _MEM_TYPE_SLICE 1U /* VPU R, CAAM W */ +#define _MEM_TYPE_DPB 2U /* VPU RW, Render R */ +#define _MEM_TYPE_VPU_WORKING 3U /* VPU R, CPU W. non-secure memory */ +#define _MEM_TYPE_VPU_WORKING_SPECIAL 4U /* VPU R, CPU RW. only for VP9 counter context table */ +#define _MEM_TYPE_VPU_ONLY 5U /* VPU RW only. */ + +/* + memory type enum +*/ +typedef enum { + MEM_TYPE_SHARED = 0, + MEM_TYPE_DEVICE, +}_mem_type; + +/* + memory region id enum, also as index of array "_regions" and array "hlina_mgr" +*/ +typedef enum { + MEM_ID_SHARED = 0, + MEM_ID_0, +/* MEM_ID_1, */ + MAX_MEM_REGION +}_mem_region_id; + +/* + memory region properties struct +*/ +typedef struct __mem_region { + unsigned long long bus_addr; /* Physical address of dev memory region */ + char * virt_addr; /* virtual address mapping to CPU, 0 means invisible to CPU */ + unsigned long long size; /* size of dev memory region */ + unsigned short bank; /* DDR RAM bank id */ + _mem_type type; + unsigned long translation_offset; /* user space SW will substract HLINA_TRANSL_OFFSET from the bus address + * and decoder HW will use the result as the address translated base + * address. The SW needs the original host memory bus address for memory + * mapping to virtual address. */ +} _mem_region_t; + +/* + descriptor for each chunk +*/ +typedef struct hlinc { + unsigned short chunks_occupied; /* indicate number of occupied chunks which start from current chunk */ + int filp; /* Client that allocated this chunk */ +} hlina_chunk_t; + +/* + memory region management struct +*/ +typedef struct __hlina_mgr { + hlina_chunk_t *hlina_chunks; /* point to descriptor of each chunks */ + unsigned long chunks; /* total chunks in this mem region */ + unsigned long reserved_chunks; /* number of reserved chunks in this mem rtegion. + * The reserved chunks is located at the beginning of corresponding mem region, application can't use them */ + _mem_region_t *mem_prop; +} _hlina_mgr_t; + + +/* + memory regions perporties, indexed by _mem_region_id. +*/ +static _mem_region_t _regions[MAX_MEM_REGION] = { + /*MEM_ID_SHARED*/ { SHARED_MEM_DEV_ADDRESS, (char *)SHARED_MEM_CPU_ADDRESS, SHARED_MEM_SIZE, SHARED_MEM_BANK, MEM_TYPE_SHARED, 0 }, + /*MEM_ID_0 */ { MEM_0_DEV_ADDRESS, (char *)MEM_0_CPU_ADDRESS, MEM_0_SIZE, MEM_0_BANK, MEM_TYPE_DEVICE, 0 } + /*MEM_ID_1 { MEM_1_DEV_ADDRESS, (char *)MEM_1_CPU_ADDRESS, MEM_1_SIZE, MEM_1_BANK, MEM_TYPE_DEVICE, 0 } */ +}; + +/* + memory regions hlina manager, indexed by _mem_region_id. +*/ +static _hlina_mgr_t hlina_mgr[MAX_MEM_REGION]; + +int memalloc_major = -1; +//static DEFINE_SPINLOCK(mem_lock); +pthread_mutex_t mem_lock = PTHREAD_MUTEX_INITIALIZER; +/* + Functions declaration +*/ +static int AllocMemory(unsigned long long *busaddr, unsigned int size, unsigned int region, const int filp); +static int FreeMemory(unsigned long long busaddr, const int filp); +//static void * DirectMemoryMap(unsigned long long busaddr, unsigned long map_size); +static void CalculateChunks(_mem_region_id id, unsigned long *chunks, unsigned long *chunks_mgr_size); +static void ResetMems(void); + +/* + To specified mem region, calculate total chunks number and aligned buffer size to store chunks' descriptors. +*/ +static void CalculateChunks(_mem_region_id id, unsigned long *chunks, unsigned long *chunks_mgr_size) +{ + *chunks = 0; + *chunks_mgr_size = 0; + + if(id < MAX_MEM_REGION) { + *chunks = _regions[id].size / CHUNK_SIZE; + *chunks_mgr_size = (((unsigned int)*chunks) * sizeof(hlina_chunk_t) + CHUNK_SIZE - 1) & (~(CHUNK_SIZE - 1)); + } +} + +/* + To specified bus address, convert it to virtual (CPU) address according to mem region properties. +*/ +void * DirectMemoryMap(unsigned long long busaddr, unsigned long map_size) { + int i; + unsigned long long offset; + void *virtaddr = NULL; + u32 busAddrRight = 0; + + if(map_size == 0) { + return NULL; + } + + for(i=0; i= _regions[i].bus_addr) && (busaddr <= _regions[i].bus_addr + _regions[i].size)) { + busAddrRight = 1; + if(_regions[i].virt_addr == NULL) { + continue; + } + else { + offset = busaddr - _regions[i].bus_addr; + if ((offset + map_size) <= _regions[i].size) { + virtaddr = (void *)(_regions[i].virt_addr + offset); + break; + } + } + } + } + if((!busAddrRight) && (virtaddr==NULL)) { + return (void *)(unsigned long)busaddr; + } + + return virtaddr; +} + +/* + ioctl +*/ +long memalloc_ioctl(int filp, unsigned int cmd, void *arg) { + int ret = 0; + MemallocParams memparams; + addr_t busaddr; + unsigned int region; + + PDEBUG("memalloc ioctl cmd 0x%08x\n", cmd); + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != MEMALLOC_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > MEMALLOC_IOC_MAXNR) return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd)); + if (ret) return -EFAULT; + + spin_lock(&mem_lock); + + switch (cmd) { + case MEMALLOC_IOCHARDRESET: + PDEBUG("memalloc HARDRESET\n"); + ResetMems(); + break; + case MEMALLOC_IOCXGETBUFFER: + PDEBUG("memalloc GETBUFFER"); + + ret = copy_from_user(&memparams, (MemallocParams *)arg, + sizeof(MemallocParams)); + if (ret) break; + + region = MEM_ID_SHARED; + if(memparams.mem_type == _MEM_TYPE_DPB || memparams.mem_type == _MEM_TYPE_VPU_ONLY) +#ifndef MEM_ONLY_DEV_CHECK + region = MEM_ID_SHARED; //For get output buffer for CPU +#else + region = MEM_ID_0; +#endif + ret = AllocMemory(&memparams.bus_address, memparams.size, region, filp); + + memparams.translation_offset = _regions[region].translation_offset; + + ret |= copy_to_user((MemallocParams *)arg, &memparams, + sizeof(MemallocParams)); + + break; + case MEMALLOC_IOCSFREEBUFFER: + PDEBUG("memalloc FREEBUFFER\n"); + + __get_user(busaddr, (addr_t *)arg); + ret = FreeMemory(busaddr, filp); + break; + } + + spin_unlock(&mem_lock); + + return ret ? -EFAULT : 0; +} + +int memalloc_open(int *inode, int filp) { + PDEBUG("memalloc dev opened\n"); + return 0; +} + +int memalloc_release(int *inode, int filp) { + int i = 0, j = 0; + hlina_chunk_t *p_hlina; + + for (i = 0; i < MAX_MEM_REGION; i++) { + p_hlina = hlina_mgr[i].hlina_chunks; + for (j = hlina_mgr[i].reserved_chunks; j < hlina_mgr[i].chunks; j++) { + spin_lock(&mem_lock); + if (p_hlina[j].filp == filp) { + printk(KERN_WARNING "memalloc: Found unfreed memory at release time!\n"); + + p_hlina[j].filp = 0; + p_hlina[j].chunks_occupied = 0; + } + spin_unlock(&mem_lock); + } + } + PDEBUG("memalloc dev closed\n"); + return 0; +} + +int __init memalloc_init(void) { + int result; + int i; + unsigned long chunks, mgr_size, reserved_size; + void *p; + + + PDEBUG("memalloc module init\n"); + printk("memalloc: Linear Memory Allocator\n"); + + /* + Allocate memory for hlina_mgr + In NETINT platform, there is no big heap memory to store hlina_mgr, so it is put to beginning of shared memory region directly + */ + reserved_size = 0; + for (i=0; i= MAX_MEM_REGION) { + printk("memalloc: Allocation FAILED: unknown region = %u\n", region); + return -EFAULT; + } + + p_hlina = hlina_mgr[region].hlina_chunks; + total_chunks = hlina_mgr[region].chunks; + + /* run through the chunk table */ + for (i = hlina_mgr[region].reserved_chunks; i < total_chunks;) { + skip_chunks = 0; + /* if this chunk is available */ + if (!p_hlina[i].chunks_occupied) { + /* check that there is enough memory left */ + if (i + alloc_chunks > total_chunks) break; + + /* check that there is enough consecutive chunks available */ + for (j = i; j < i + alloc_chunks; j++) { + if (p_hlina[j].chunks_occupied) { + skip_chunks = 1; + /* skip the used chunks */ + i = j + p_hlina[j].chunks_occupied; + break; + } + } + + /* if enough free memory found */ + if (!skip_chunks) { + *busaddr = hlina_mgr[region].mem_prop->bus_addr + i * CHUNK_SIZE; + p_hlina[i].filp = filp; + p_hlina[i].chunks_occupied = alloc_chunks; + break; + } + } else { + /* skip the used chunks */ + i += p_hlina[i].chunks_occupied; + } + } + + if (*busaddr == 0) { + printk("memalloc: Allocation FAILED: size = %u\n", size); + return -EFAULT; + } else { + PDEBUG("MEMALLOC OK: size: %d, reserved: %d\n", size, + alloc_chunks * CHUNK_SIZE); + } + + return 0; +} + +/* Free a buffer based on bus address */ +static int FreeMemory(unsigned long long busaddr, const int filp) { + unsigned int i, id; + unsigned long long addr; + hlina_chunk_t *p_hlina; + + for(i = 0; i < MAX_MEM_REGION; i++) { + addr = busaddr + _regions[i].translation_offset; + if ((addr >= _regions[i].bus_addr) && (addr < _regions[i].bus_addr + _regions[i].size)) { + id = (addr - _regions[i].bus_addr) / CHUNK_SIZE; + p_hlina = &hlina_mgr[i].hlina_chunks[id]; + if ((p_hlina->chunks_occupied) && (p_hlina->filp == filp)) { + p_hlina->filp = 0; + p_hlina->chunks_occupied = 0; + return 0; + } + } + } + + printk(KERN_WARNING "memalloc: Owner mismatch while freeing memory!\n"); + return -1; +} + + +/* Reset "used" status */ +static void ResetMems(void) { + int i, j = 0; + hlina_chunk_t *p_hlina; + + for (i=0; i + +#undef PDEBUG +#ifdef HANTRODEC_DEBUG +# ifdef __KERNEL__ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hantrodec: " fmt, ## args) +# else +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) +#endif + +/******************************************************************************/ +/* subsystem configuration */ +/******************************************************************************/ + +/* List of subsystems */ +static struct SubsysDesc subsys_array[] = { + /* {slice_index, index, base} */ + {0, 0, 0x02310000}, +// {0, 1, 0x02350000} +}; + +/* List of all HW cores. */ +static struct CoreDesc core_array[] = { + /* {slice, subsys, core_type, offset, iosize, irq, has_apb} */ +#if 0 + {0, 0, HW_VC8000DJ, 0x600000, 0, 0}, + {0, 0, HW_VC8000D, 0x602000, 0, 0}, + {0, 0, HW_L2CACHE, 0x604000, 0, 0}, + {0, 0, HW_DEC400, 0x606000, 0, 0}, + {0, 0, HW_BIGOCEAN, 0x608000, 0, 0}, + {0, 0, HW_NOC,0x60a000, 0, 0}, + {0, 0, HW_AXIFE, 0x60c000, 0, 0} +#endif + {0, 0, HW_VCMD, 0x0, 27*4, 0, -1}, + {0, 0, HW_VC8000D, 0x2000, 503*4, -1, -1}, +// {0, 0, HW_MMU, 0x3000, 228*4, -1, 0}, +// {0, 0, HW_MMU_WR, 0x4000, 228*4, -1, 0}, + {0, 0, HW_DEC400, 0x6000, 1568*4, -1, -1}, + {0, 0, HW_L2CACHE, 0x4000, 231*4, -1, -1}, +// {0, 0, HW_AXIFE, 0x5000, 64*4, -1, 1}, + +// {0, 1, HW_VCMD, 0x0, 27*4, -1, 0}, +// {0, 1, HW_VC8000D, 0x2000, 503*4, -1, 1}, +// {0, 1, HW_MMU, 0x3000, 228*4, -1, 0}, +// {0, 1, HW_MMU_WR, 0x4000, 228*4, -1, 0}, +// {0, 1, HW_DEC400, 0x6000, 1568*4, -1, 0}, +// {0, 1, HW_L2CACHE, 0x4000, 231*4, -1, 0}, +// {0, 1, HW_AXIFE, 0x5000, 64*4, -1, 1}, +}; + +extern struct vcmd_config vcmd_core_array[MAX_SUBSYS_NUM]; +extern int total_vcmd_core_num; +extern unsigned long multicorebase[]; +extern int irq[]; +extern unsigned int iosize[]; +extern int reg_count[]; + +/* + If VCMD is used, convert core_array to vcmd_core_array, which are used in + hantor_vcmd_xxx.c. + Otherwise, covnert core_array to multicore_base/irq/iosize, which are used in + hantro_dec_xxx.c + + VCMD: + - struct vcmd_config vcmd_core_array[MAX_SUBSYS_NUM] + - total_vcmd_core_num + + NON-VCMD: + - multicorebase[HXDEC_MAX_CORES] + - irq[HXDEC_MAX_CORES] + - iosize[HXDEC_MAX_CORES] +*/ +void CheckSubsysCoreArray(struct subsys_config *subsys, int *vcmd) { + int num = sizeof(subsys_array)/sizeof(subsys_array[0]); + int i, j; + + memset(subsys, 0, sizeof(subsys[0])*MAX_SUBSYS_NUM); + for (i = 0; i < num; i++) { + subsys[i].base_addr = subsys_array[i].base; + subsys[i].irq = -1; + for (j = 0; j < HW_CORE_MAX; j++) { + subsys[i].submodule_offset[j] = 0xffff; + subsys[i].submodule_iosize[j] = 0; + subsys[i].submodule_hwregs[j] = NULL; + } + } + + total_vcmd_core_num = 0; + + for (i = 0; i < sizeof(core_array)/sizeof(core_array[0]); i++) { + if (!subsys[core_array[i].subsys].base_addr) { + /* undefined subsystem */ + continue; + } + subsys[core_array[i].subsys].submodule_offset[core_array[i].core_type] + = core_array[i].offset; + subsys[core_array[i].subsys].submodule_iosize[core_array[i].core_type] + = core_array[i].iosize; + if (core_array[i].irq != -1) { + subsys[core_array[i].subsys].irq = core_array[i].irq; + } + subsys[core_array[i].subsys].has_apbfilter[core_array[i].core_type] = core_array[i].has_apb; + /* vcmd found */ + if (core_array[i].core_type == HW_VCMD) { + *vcmd = 1; + total_vcmd_core_num++; + } + } + + printk(KERN_INFO "hantrodec: vcmd = %d\n", *vcmd); + + /* To plug into hantro_vcmd.c */ + if (*vcmd) { + for (i = 0; i < total_vcmd_core_num; i++) { + vcmd_core_array[i].vcmd_base_addr = subsys[i].base_addr; + vcmd_core_array[i].vcmd_iosize = subsys[i].submodule_iosize[HW_VCMD]; + vcmd_core_array[i].vcmd_irq = subsys[i].irq; + vcmd_core_array[i].sub_module_type = 2; /* TODO(min): to be fixed */ + vcmd_core_array[i].submodule_main_addr = subsys[i].submodule_offset[HW_VC8000D]; + vcmd_core_array[i].submodule_dec400_addr = subsys[i].submodule_offset[HW_DEC400]; + vcmd_core_array[i].submodule_L2Cache_addr = subsys[i].submodule_offset[HW_L2CACHE]; + vcmd_core_array[i].submodule_MMU_addr = subsys[i].submodule_offset[HW_MMU]; + } + } + memset(multicorebase, 0, sizeof(multicorebase[0]) * HXDEC_MAX_CORES); + for (i = 0; i < num; i++) { + multicorebase[i] = subsys[i].base_addr + subsys[i].submodule_offset[HW_VC8000D]; + irq[i] = subsys[i].irq; + iosize[i] = subsys[i].submodule_iosize[HW_VC8000D]; + printk(KERN_INFO "hantrodec: [%d] multicorebase 0x%08lx, iosize %d\n", i, multicorebase[i], iosize[i]); + } +} + + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.c new file mode 100644 index 000000000..0ced631fb --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.c @@ -0,0 +1,151 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#include "osal.h" +#include "user_freertos.h" +#include "io_tools.h" +#include "memalloc_freertos.h" + +static pthread_mutex_t vcmd_dev_open_count_mutex = PTHREAD_MUTEX_INITIALIZER; +static u32 vcmd_dev_open_count = 0; +extern int vcmd; //it is defined in hantro_dec_freertos.c + +/** + * Unify driver for vcmd, the related vcmd interface besides init/open/release/ + * ioctl will be called in the hantrdec_init/open/release/ioctl when vcmd is used. + * All drivers's probe should be added and be called by main() explicitly + */ +int Platform_init() { + //Firstly, initialize the mem for IO device + if (memalloc_init() != 0) { + PDEBUG("memalloc_init error\n"); + assert(0); + } + + //vcmd will modify if exist vcmd device in hantrodec_init + if (hantrodec_init() != 0) { + PDEBUG("hantroenc_init error\n"); + assert(0); + } + + return 0; +} +int freertos_open(const char* dev_name, int flag) { + //do a hash operator to gain the only dev fd + //int fd = hash(name); + int fd = 0; //the default is error id + if(!strcmp(dev_name, DEC_MODULE_PATH)) { + fd = DEC_FD << 28; + if(vcmd) { + //u32, MSB 4bit for device type, LSB 28bit for reference count + pthread_mutex_lock(&vcmd_dev_open_count_mutex); + vcmd_dev_open_count++; + fd += vcmd_dev_open_count; + pthread_mutex_unlock(&vcmd_dev_open_count_mutex); + } + hantrodec_open(NULL, fd); + } + else if(!strcmp(dev_name, MEMALLOC_MODULE_PATH)) { + fd = MEM_FD << 28; + memalloc_open(NULL, fd); + } + return fd; +} + +void freertos_close(int fd) { + //u32, MSB 4bit for device type + u32 fd_tmp = fd; + fd_tmp = fd_tmp >> 28; + + switch(fd_tmp) { + case DEC_FD: { + hantrodec_release(NULL, fd); + break; + } + case MEM_FD: { + //so if low in special paltform, could comment it when test, but in normal, it will be opened + //memalloc_release(NULL, fd); + break; + } + case 0: //for /dev/mem, it it not used, so shouldn't go to default + break; + default: { + PDEBUG("freertos_release fd 0x%x error\n", fd); + break; + } + } +} + +long freertos_ioctl(int fd, unsigned int cmd, void *arg) { + long ret = -1; + //u32, MSB 4bit for device type + u32 fd_tmp = fd; + fd_tmp = fd_tmp >> 28; + switch(fd_tmp) { + case DEC_FD: { + ret = hantrodec_ioctl(fd, cmd, arg); + break; + } + case MEM_FD: { + ret = memalloc_ioctl(fd, cmd, arg); + break; + } + default: { + PDEBUG("freertos_ioctl fd 0x%x error\n", fd); + break; + } + } + + return ret; +} + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.h new file mode 100644 index 000000000..01e733bdd --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/freertos/user_freertos.h @@ -0,0 +1,79 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#ifndef _USER_FREERTOS_H_ +#define _USER_FREERTOS_H_ + +#include "basetype.h" + +#include // size_t +#include +#include +#include +#include +#include + +//u32, MSB 4bit for device type, LSB 28bit for reference count +enum { + ENC_FD = 0x1, + DEC_FD = 0x2, + MEM_FD = 0x3 + //TODO for other device, can exist 15 kinds of evices +}; + +int Platform_init(); +int freertos_open(const char* dev_name, int flag); +void freertos_close(int fd); +long freertos_ioctl(int filp, unsigned int cmd, void *arg); + +#endif /* _USER_FREERTOS_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_axife.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_axife.c new file mode 100644 index 000000000..22ac40b7e --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_axife.c @@ -0,0 +1,88 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)) +#include +#else +#include +#endif +#include +#include + +#include "hantroaxife.h" + +#define HANTRO_AXIFE_OFFSET 0 + +void AXIFEEnable (volatile unsigned char *hwregs) { + if (!hwregs) return; + + //AXI FE pass through + iowrite32(0x40, (void*)(hwregs + HANTRO_AXIFE_OFFSET + 0x2C)); + printk(KERN_INFO "AXI FE: 0x2C = 0x%x\n", ioread32((void*)(hwregs + 0x2C))); + iowrite32(0x2, (void*)(hwregs + HANTRO_AXIFE_OFFSET + 0x28)); + printk(KERN_INFO "AXI FE: 0x28 = 0x%x\n", ioread32((void*)(hwregs + 0x28))); +} + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_dec.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_dec.c new file mode 100644 index 000000000..fa1c864c9 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_dec.c @@ -0,0 +1,3020 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#include "hantrodec.h" +#include "dwl_defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "subsys.h" +#include "hantroaxife.h" +#include +#include +#include +#include +#include +#include +#include "kernel_allocator.h" + +#undef PDEBUG +#ifdef HANTRODEC_DEBUG +# ifdef __KERNEL__ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hantrodec: " fmt, ## args) +# else +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) +#endif + +#define PCI_VENDOR_ID_HANTRO 0x10ee// 0x1ae0//0x16c3 +#define PCI_DEVICE_ID_HANTRO_PCI 0x8014//0x001a// 0xabcd + +/* Base address DDR register */ +#define PCI_DDR_BAR 0 + +/* Base address got control register */ +#define PCI_CONTROL_BAR 4 + +/* PCIe hantro driver offset in control register */ +#define HANTRO_REG_OFFSET0 0x600000 +#define HANTRO_REG_OFFSET1 0x700000 + +/* TODO(mheikkinen) Implement multicore support. */ +struct pci_dev *gDev = NULL; /* PCI device structure. */ +unsigned long gBaseHdwr; /* PCI base register address (Hardware address) */ +unsigned long gBaseDDRHw; /* PCI base register address (memalloc) */ +u32 gBaseLen; /* Base register address Length */ + +/* hantro G1 regs config including dec and pp */ +//#define HANTRO_DEC_ORG_REGS 60 +//#define HANTRO_PP_ORG_REGS 41 + +#define HANTRO_DEC_EXT_REGS 27 +#define HANTRO_PP_EXT_REGS 9 + +//#define HANTRO_G1_DEC_TOTAL_REGS (HANTRO_DEC_ORG_REGS + HANTRO_DEC_EXT_REGS) +#define HANTRO_PP_TOTAL_REGS (HANTRO_PP_ORG_REGS + HANTRO_PP_EXT_REGS) +#define HANTRO_G1_DEC_REGS 155 /*G1 total regs*/ + +//#define HANTRO_DEC_ORG_FIRST_REG 0 +//#define HANTRO_DEC_ORG_LAST_REG 59 +//#define HANTRO_DEC_EXT_FIRST_REG 119 +//#define HANTRO_DEC_EXT_LAST_REG 145 + +#define HANTRO_PP_ORG_FIRST_REG 60 +#define HANTRO_PP_ORG_LAST_REG 100 +#define HANTRO_PP_EXT_FIRST_REG 146 +#define HANTRO_PP_EXT_LAST_REG 154 + +/* hantro G2 reg config */ +#define HANTRO_G2_DEC_REGS 337 /*G2 total regs*/ +#define HANTRO_G2_DEC_FIRST_REG 0 +#define HANTRO_G2_DEC_LAST_REG HANTRO_G2_DEC_REGS-1 + +/* hantro VC8000D reg config */ +#define HANTRO_VC8000D_REGS 503 /*VC8000D total regs*/ +#define HANTRO_VC8000D_FIRST_REG 0 +#define HANTRO_VC8000D_LAST_REG HANTRO_VC8000D_REGS-1 +#define HANTRODEC_HWBUILD_ID_OFF (309 * 4) + +/* Logic module IRQs */ +#define HXDEC_NO_IRQ -1 + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define DEC_IO_SIZE_MAX (MAX(MAX(HANTRO_G2_DEC_REGS, HANTRO_G1_DEC_REGS), HANTRO_VC8000D_REGS) * 4) + +/* User should modify these configuration if do porting to own platform. */ +/* Please guarantee the base_addr, io_size, dec_irq belong to same core. */ + +/* Defines use kernel clk cfg or not**/ +//#define CLK_CFG +#ifdef CLK_CFG +#define CLK_ID "hantrodec_clk" /*this id should conform with platform define*/ +#endif + +/* Logic module base address */ +#define SOCLE_LOGIC_0_BASE 0x38300000 +#define SOCLE_LOGIC_1_BASE 0x38310000 + +#define VEXPRESS_LOGIC_0_BASE 0xFC010000 +#define VEXPRESS_LOGIC_1_BASE 0xFC020000 + +#define DEC_IO_SIZE_0 DEC_IO_SIZE_MAX /* bytes */ +#define DEC_IO_SIZE_1 DEC_IO_SIZE_MAX /* bytes */ + +#define DEC_IRQ_0 HXDEC_NO_IRQ +#define DEC_IRQ_1 HXDEC_NO_IRQ + +#define IS_G1(hw_id) (((hw_id) == 0x6731)? 1 : 0) +#define IS_G2(hw_id) (((hw_id) == 0x6732)? 1 : 0) +#define IS_VC8000D(hw_id) (((hw_id) == 0x8001)? 1 : 0) +#define IS_BIGOCEAN(hw_id) (((hw_id) == 0xB16D)? 1 : 0) + +/* Some IPs HW configuration paramters for APB Filter */ +/* Because now such information can't be read from APB filter configuration registers */ +/* The fixed value have to be used */ +#define VC8000D_NUM_MASK_REG 336 +#define VC8000D_NUM_MODE 4 +#define VC8000D_MASK_REG_OFFSET 4096 +#define VC8000D_MASK_BITS_PER_REG 1 + +#define VC8000DJ_NUM_MASK_REG 332 +#define VC8000DJ_NUM_MODE 1 +#define VC8000DJ_MASK_REG_OFFSET 4096 +#define VC8000DJ_MASK_BITS_PER_REG 1 + +#define AV1_NUM_MASK_REG 303 +#define AV1_NUM_MODE 1 +#define AV1_MASK_REG_OFFSET 4096 +#define AV1_MASK_BITS_PER_REG 1 + +#define AXIFE_NUM_MASK_REG 144 +#define AXIFE_NUM_MODE 1 +#define AXIFE_MASK_REG_OFFSET 4096 +#define AXIFE_MASK_BITS_PER_REG 1 + +#define VC8000D_MAX_CONFIG_LEN 32 + +#define VC8000D_PM_TIMEOUT 100 /* ms */ +/*************************************************************/ + +/*********************local variable declaration*****************/ + +static const int DecHwId[] = { + 0x6731, /* G1 */ + 0x6732, /* G2 */ + 0xB16D, /* BigOcean */ + 0x8001 /* VC8000D */ +}; + +unsigned long base_port = -1; +unsigned int pcie = 0; +volatile unsigned char *reg = NULL; +unsigned int reg_access_opt = 0; +unsigned int vcmd = 0; +unsigned long alloc_size = 512; +unsigned long alloc_base = 0x1c0000000; + +unsigned long multicorebase[HXDEC_MAX_CORES] = { + HANTRO_REG_OFFSET0, + HANTRO_REG_OFFSET1, + 0, + 0 +}; + +int irq[HXDEC_MAX_CORES] = { + 131, + DEC_IRQ_1, + -1, + -1 +}; + +unsigned int iosize[HXDEC_MAX_CORES] = { + DEC_IO_SIZE_0, + DEC_IO_SIZE_1, + -1, + -1 +}; + +/* Because one core may contain multi-pipeline, so multicore base may be changed */ +unsigned long multicorebase_actual[HXDEC_MAX_CORES]; + +struct subsys_config vpu_subsys[MAX_SUBSYS_NUM]; + +struct apbfilter_cfg apbfilter_cfg[MAX_SUBSYS_NUM][HW_CORE_MAX]; + +struct axife_cfg axife_cfg[MAX_SUBSYS_NUM]; +int elements = 2; + +#ifdef CLK_CFG +struct clk *clk_cfg; +int is_clk_on; +struct timer_list timer; +#endif + +/* module_param(name, type, perm) */ +//module_param(base_port, ulong, 0); +module_param(pcie, uint, 0); +//module_param_array(irq, int, &elements, 0); +module_param_array(multicorebase, ulong, &elements, 0644); +module_param(reg_access_opt, uint, 0); +module_param(vcmd, uint, 0); +module_param(alloc_base, ulong, 0); +module_param(alloc_size, ulong, 0); + + +static int hantrodec_major = 0; /* dynamic allocation */ +static int hantrodec_minor = 0; /* dynamic allocation */ +static struct cdev hantrodec_cdev; +static dev_t hantrodec_devt; +static struct class *hantrodec_class; + +static struct dentry *root_debugfs_dir = NULL; + +/* here's all the must remember stuff */ +typedef struct { + char *buffer; + volatile unsigned int iosize[HXDEC_MAX_CORES]; + /* mapped address to different HW cores regs*/ + volatile u8 *hwregs[HXDEC_MAX_CORES][HW_CORE_MAX]; + /* mapped address to different HW cores regs*/ + volatile u8 *apbfilter_hwregs[HXDEC_MAX_CORES][HW_CORE_MAX]; + volatile int irq[HXDEC_MAX_CORES]; + int hw_id[HXDEC_MAX_CORES][HW_CORE_MAX]; + /* Requested client type for given core, used when a subsys has multiple + decoders, e.g., VC8000D+VC8000DJ+BigOcean */ + int client_type[HXDEC_MAX_CORES]; + int cores; + struct fasync_struct *async_queue_dec; + struct fasync_struct *async_queue_pp; + struct platform_device *pdev; + struct clk *cclk; + struct clk *aclk; + struct clk *pclk; + char config_buf[VC8000D_MAX_CONFIG_LEN]; + int has_power_domains; +} hantrodec_t; + +typedef struct { + u32 cfg[HXDEC_MAX_CORES]; /* indicate the supported format */ + u32 cfg_backup[HXDEC_MAX_CORES]; /* back up of cfg */ + int its_main_core_id[HXDEC_MAX_CORES]; /* indicate if main core exist */ + int its_aux_core_id[HXDEC_MAX_CORES]; /* indicate if aux core exist */ +} core_cfg; + +static hantrodec_t hantrodec_data; /* dynamic allocation? */ + +static int ReserveIO(void); +static void ReleaseIO(void); + +static void ResetAsic(hantrodec_t * dev); + +#ifdef HANTRODEC_DEBUG +static void dump_regs(hantrodec_t *dev); +#endif + +/* IRQ handler */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static irqreturn_t hantrodec_isr(int irq, void *dev_id, struct pt_regs *regs); +#else +static irqreturn_t hantrodec_isr(int irq, void *dev_id); +#endif + +static u32 dec_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX/4]; +static u32 apbfilter_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX/4+1]; +/* shadow_regs used to compare whether it's necessary to write to registers */ +static u32 shadow_dec_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX/4]; + +struct semaphore dec_core_sem; +struct semaphore pp_core_sem; + +static int dec_irq = 0; +static int pp_irq = 0; + +atomic_t irq_rx = ATOMIC_INIT(0); +atomic_t irq_tx = ATOMIC_INIT(0); + +static struct file* dec_owner[HXDEC_MAX_CORES]; +static struct file* pp_owner[HXDEC_MAX_CORES]; +static int CoreHasFormat(const u32 *cfg, int core, u32 format); + +/* spinlock_t owner_lock = SPIN_LOCK_UNLOCKED; */ +DEFINE_SPINLOCK(owner_lock); + +DECLARE_WAIT_QUEUE_HEAD(dec_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(pp_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(hw_queue); +#ifdef CLK_CFG +DEFINE_SPINLOCK(clk_lock); +#endif + +#define DWL_CLIENT_TYPE_H264_DEC 1U +#define DWL_CLIENT_TYPE_MPEG4_DEC 2U +#define DWL_CLIENT_TYPE_JPEG_DEC 3U +#define DWL_CLIENT_TYPE_PP 4U +#define DWL_CLIENT_TYPE_VC1_DEC 5U +#define DWL_CLIENT_TYPE_MPEG2_DEC 6U +#define DWL_CLIENT_TYPE_VP6_DEC 7U +#define DWL_CLIENT_TYPE_AVS_DEC 8U +#define DWL_CLIENT_TYPE_RV_DEC 9U +#define DWL_CLIENT_TYPE_VP8_DEC 10U +#define DWL_CLIENT_TYPE_VP9_DEC 11U +#define DWL_CLIENT_TYPE_HEVC_DEC 12U +#define DWL_CLIENT_TYPE_ST_PP 14U +#define DWL_CLIENT_TYPE_H264_MAIN10 15U +#define DWL_CLIENT_TYPE_AVS2_DEC 16U +#define DWL_CLIENT_TYPE_AV1_DEC 17U +#define DWL_CLIENT_TYPE_BO_AV1_DEC 31U + +#define BIGOCEANDEC_CFG 1 +#define BIGOCEANDEC_AV1_E 5 + +static core_cfg config; + +static void ReadCoreConfig(hantrodec_t *dev) { + int c, j; + u32 reg, tmp, mask; + + memset(config.cfg, 0, sizeof(config.cfg)); + + for(c = 0; c < dev->cores; c++) { + for (j = 0; j < HW_CORE_MAX; j++) { + if (j != HW_VC8000D && j != HW_VC8000DJ && j != HW_BIGOCEAN) + continue; + if (!dev->hwregs[c][j]) /* NOT defined core type */ + continue; + /* Decoder configuration */ + if (IS_G1(dev->hw_id[c][j])) { + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_H264_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has H264\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_JPEG_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has JPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_HJPEG_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has HJPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_MPEG4_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has MPEG4\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0; + + tmp = (reg >> DWL_VC1_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has VC1\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC: 0; + + tmp = (reg >> DWL_MPEG2_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has MPEG2\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0; + + tmp = (reg >> DWL_VP6_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has VP6\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG_2 * 4)); + + /* VP7 and WEBP is part of VP8 */ + mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E); + tmp = (reg & mask); + if(tmp & (1 << DWL_VP8_E)) + pr_info("hantrodec: subsys[%d] has VP8\n", c); + if(tmp & (1 << DWL_VP7_E)) + pr_info("hantrodec: subsys[%d] has VP7\n", c); + if(tmp & (1 << DWL_WEBP_E)) + pr_info("hantrodec: subsys[%d] has WebP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0; + + tmp = (reg >> DWL_AVS_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has AVS\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC: 0; + + tmp = (reg >> DWL_RV_E) & 0x03U; + if(tmp) pr_info("hantrodec: subsys[%d] has RV\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c][j] + HANTROPP_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_G1_PP_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + } else if((IS_G2(dev->hw_id[c][j]))) { + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_CFG_STAT * 4)); + + tmp = (reg >> DWL_G2_HEVC_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has HEVC\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0; + + tmp = (reg >> DWL_G2_VP9_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has VP9\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODECPP_SYNTH_CFG * 4)); + + tmp = (reg >> DWL_G2_PP_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + } else if((IS_VC8000D(dev->hw_id[c][j])) && config.its_main_core_id[c] < 0) { + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG * 4)); + pr_info("hantrodec: subsys[%d] swreg[%d] = 0x%08x\n", c, HANTRODEC_SYNTH_CFG, reg); + + tmp = (reg >> DWL_H264_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has H264\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_H264HIGH10_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has H264HIGH10\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_AVS2_E) & 0x03U; + if(tmp) pr_info("hantrodec: subsys[%d] has AVS2\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS2_DEC : 0; + + tmp = (reg >> DWL_JPEG_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has JPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_HJPEG_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has HJPEG\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + + tmp = (reg >> DWL_MPEG4_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has MPEG4\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0; + + tmp = (reg >> DWL_VC1_E) & 0x3U; + if(tmp) pr_info("hantrodec: subsys[%d] has VC1\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC: 0; + + tmp = (reg >> DWL_MPEG2_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has MPEG2\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0; + + tmp = (reg >> DWL_VP6_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has VP6\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG_2 * 4)); + pr_info("hantrodec: subsys[%d] swreg[%d] = 0x%08x\n", c, HANTRODEC_SYNTH_CFG_2, reg); + + /* VP7 and WEBP is part of VP8 */ + mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E); + tmp = (reg & mask); + if(tmp & (1 << DWL_VP8_E)) + pr_info("hantrodec: subsys[%d] has VP8\n", c); + if(tmp & (1 << DWL_VP7_E)) + pr_info("hantrodec: subsys[%d] has VP7\n", c); + if(tmp & (1 << DWL_WEBP_E)) + pr_info("hantrodec: subsys[%d] has WebP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0; + + tmp = (reg >> DWL_AVS_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has AVS\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC: 0; + + tmp = (reg >> DWL_RV_E) & 0x03U; + if(tmp) pr_info("hantrodec: subsys[%d] has RV\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0; + + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG_3 * 4)); + pr_info("hantrodec: subsys[%d] swreg[%d] = 0x%08x\n", c, HANTRODEC_SYNTH_CFG_3, reg); + + tmp = (reg >> DWL_HEVC_E) & 0x07U; + if(tmp) pr_info("hantrodec: subsys[%d] has HEVC\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0; + + tmp = (reg >> DWL_VP9_E) & 0x07U; + if(tmp) pr_info("hantrodec: subsys[%d] has VP9\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0; + + /* Post-processor configuration */ + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODECPP_CFG_STAT * 4)); + + tmp = (reg >> DWL_PP_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has PP\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; + + config.cfg[c] |= 1 << DWL_CLIENT_TYPE_ST_PP; + + if (config.its_aux_core_id[c] >= 0) { + /* set main_core_id and aux_core_id */ + reg = ioread32((void*)(dev->hwregs[c][j] + HANTRODEC_SYNTH_CFG_2 * 4)); + + tmp = (reg >> DWL_H264_PIPELINE_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has pipeline H264\n", c); + config.cfg[config.its_aux_core_id[c]] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; + + tmp = (reg >> DWL_JPEG_PIPELINE_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has pipeline JPEG\n", c); + config.cfg[config.its_aux_core_id[c]] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; + } + } else if (IS_BIGOCEAN(dev->hw_id[c][j])) { + reg = ioread32((void*)(dev->hwregs[c][j] + BIGOCEANDEC_CFG * 4)); + + tmp = (reg >> BIGOCEANDEC_AV1_E) & 0x01U; + if(tmp) pr_info("hantrodec: subsys[%d] has AV1 (BigOcean)\n", c); + config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_BO_AV1_DEC : 0; + } + } + } + memcpy(config.cfg_backup, config.cfg, sizeof(config.cfg)); +} + +static int CoreHasFormat(const u32 *cfg, int core, u32 format) { + return (cfg[core] & (1 << format)) ? 1 : 0; +} + +int GetDecCore(long core, hantrodec_t *dev, struct file* filp, unsigned long format) { + int success = 0; + unsigned long flags; + + spin_lock_irqsave(&owner_lock, flags); + if(CoreHasFormat(config.cfg, core, format) && dec_owner[core] == NULL /*&& config.its_main_core_id[core] >= 0*/) { + dec_owner[core] = filp; + success = 1; + + /* If one main core takes one format which doesn't supported by aux core, set aux core's cfg to none video format support */ + if (config.its_aux_core_id[core] >= 0 && + !CoreHasFormat(config.cfg, config.its_aux_core_id[core], format)) { + config.cfg[config.its_aux_core_id[core]] = 0; + } + /* If one aux core takes one format, set main core's cfg to aux core supported video format */ + else if (config.its_main_core_id[core] >= 0) { + config.cfg[config.its_main_core_id[core]] = config.cfg[core]; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return success; +} + +int GetDecCoreAny(long *core, hantrodec_t *dev, struct file* filp, + unsigned long format) { + int success = 0; + long c; + + *core = -1; + + for(c = 0; c < dev->cores; c++) { + /* a free core that has format */ + if(GetDecCore(c, dev, filp, format)) { + success = 1; + *core = c; + break; + } + } + + return success; +} + +int GetDecCoreID(hantrodec_t *dev, struct file* filp, + unsigned long format) { + long c; + unsigned long flags; + + int core_id = -1; + + for(c = 0; c < dev->cores; c++) { + /* a core that has format */ + spin_lock_irqsave(&owner_lock, flags); + if(CoreHasFormat(config.cfg, c, format)) { + core_id = c; + spin_unlock_irqrestore(&owner_lock, flags); + break; + } + spin_unlock_irqrestore(&owner_lock, flags); + } + return core_id; +} + +#if 0 +static int hantrodec_choose_core(int is_g1) { + volatile unsigned char *reg = NULL; + unsigned int blk_base = 0x38320000; + + PDEBUG("hantrodec_choose_core\n"); + if (!request_mem_region(blk_base, 0x1000, "blk_ctl")) { + pr_info("blk_ctl: failed to reserve HW regs\n"); + return -EBUSY; + } + + reg = (volatile u8 *) ioremap_nocache(blk_base, 0x1000); + + if (reg == NULL ) { + pr_info("blk_ctl: failed to ioremap HW regs\n"); + if (reg) + iounmap((void *)reg); + release_mem_region(blk_base, 0x1000); + return -EBUSY; + } + + // G1 use, set to 1; G2 use, set to 0, choose the one you are using + if (is_g1) + iowrite32(0x1, (void*)(reg + 0x14)); // VPUMIX only use G1, user should modify the reg according to platform design + else + iowrite32(0x0, (void*)(reg + 0x14)); // VPUMIX only use G2, user should modify the reg according to platform design + + if (reg) + iounmap((void *)reg); + release_mem_region(blk_base, 0x1000); + PDEBUG("hantrodec_choose_core OK!\n"); + return 0; +} +#endif + +long ReserveDecoder(hantrodec_t *dev, struct file* filp, unsigned long format) { + long core = -1; + + /* reserve a core */ + if (down_interruptible(&dec_core_sem)) + return -ERESTARTSYS; + + /* lock a core that has specific format*/ + if(wait_event_interruptible(hw_queue, + GetDecCoreAny(&core, dev, filp, format) != 0 )) + return -ERESTARTSYS; + +#if 0 + if(IS_G1(dev->hw_id[core])) { + if (0 == hantrodec_choose_core(1)) + printk("G1 is reserved\n"); + else + return -1; + } else { + if (0 == hantrodec_choose_core(0)) + printk("G2 is reserved\n"); + else + return -1; + } +#endif + + dev->client_type[core] = format; + return core; +} + +void ReleaseDecoder(hantrodec_t *dev, long core) { + u32 status; + unsigned long flags; + + PDEBUG("ReleaseDecoder %ld\n", core); + + if (dev->client_type[core] == DWL_CLIENT_TYPE_BO_AV1_DEC) + status = ioread32((void*)(dev->hwregs[core][HW_BIGOCEAN] + BIGOCEAN_IRQ_STAT_DEC_OFF)); + else + status = ioread32((void*)(dev->hwregs[core][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + + /* make sure HW is disabled */ + if(status & HANTRODEC_DEC_E) { + pr_info("hantrodec: DEC[%li] still enabled -> reset\n", core); + + /* abort decoder */ + status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void*)(dev->hwregs[core][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + } + + spin_lock_irqsave(&owner_lock, flags); + + /* If aux core released, revert main core's config back */ + if (config.its_main_core_id[core] >= 0) { + config.cfg[config.its_main_core_id[core]] = config.cfg_backup[config.its_main_core_id[core]]; + } + + /* If main core released, revert aux core's config back */ + if (config.its_aux_core_id[core] >= 0) { + config.cfg[config.its_aux_core_id[core]] = config.cfg_backup[config.its_aux_core_id[core]]; + } + + dec_owner[core] = NULL; + + spin_unlock_irqrestore(&owner_lock, flags); + + up(&dec_core_sem); + + wake_up_interruptible_all(&hw_queue); +} + +long ReservePostProcessor(hantrodec_t *dev, struct file* filp) { + unsigned long flags; + + long core = 0; + + /* single core PP only */ + if (down_interruptible(&pp_core_sem)) + return -ERESTARTSYS; + + spin_lock_irqsave(&owner_lock, flags); + + pp_owner[core] = filp; + + spin_unlock_irqrestore(&owner_lock, flags); + + return core; +} + +void ReleasePostProcessor(hantrodec_t *dev, long core) { + unsigned long flags; + + u32 status = ioread32((void*)(dev->hwregs[core][HW_VC8000D] + HANTRO_IRQ_STAT_PP_OFF)); + + /* make sure HW is disabled */ + if(status & HANTRO_PP_E) { + pr_info("hantrodec: PP[%li] still enabled -> reset\n", core); + + /* disable IRQ */ + status |= HANTRO_PP_IRQ_DISABLE; + + /* disable postprocessor */ + status &= (~HANTRO_PP_E); + iowrite32(0x10, (void*)(dev->hwregs[core][HW_VC8000D] + HANTRO_IRQ_STAT_PP_OFF)); + } + + spin_lock_irqsave(&owner_lock, flags); + + pp_owner[core] = NULL; + + spin_unlock_irqrestore(&owner_lock, flags); + + up(&pp_core_sem); +} + +long ReserveDecPp(hantrodec_t *dev, struct file* filp, unsigned long format) { + /* reserve core 0, DEC+PP for pipeline */ + unsigned long flags; + + long core = 0; + + /* check that core has the requested dec format */ + if(!CoreHasFormat(config.cfg, core, format)) + return -EFAULT; + + /* check that core has PP */ + if(!CoreHasFormat(config.cfg, core, DWL_CLIENT_TYPE_PP)) + return -EFAULT; + + /* reserve a core */ + if (down_interruptible(&dec_core_sem)) + return -ERESTARTSYS; + + /* wait until the core is available */ + if(wait_event_interruptible(hw_queue, + GetDecCore(core, dev, filp, format) != 0)) { + up(&dec_core_sem); + return -ERESTARTSYS; + } + + if (down_interruptible(&pp_core_sem)) { + ReleaseDecoder(dev, core); + return -ERESTARTSYS; + } + + spin_lock_irqsave(&owner_lock, flags); + pp_owner[core] = filp; + spin_unlock_irqrestore(&owner_lock, flags); + + return core; +} + +#ifdef HANTRODEC_DEBUG +static u32 flush_count = 0; /* times of calling of DecFlushRegs */ +static u32 flush_regs = 0; /* total number of registers flushed */ +#endif + +long DecFlushRegs(hantrodec_t *dev, struct core_desc *core) { + long ret = 0, i; +#ifdef HANTRODEC_DEBUG + int reg_wr = 2; +#endif + u32 id = core->id; + u32 type = core->type; + + PDEBUG("hantrodec: DecFlushRegs\n"); + PDEBUG("hantrodec: id = %d, type = %d, size = %d, reg_id = %d\n", + core->id, core->type, core->size, core->reg_id); + + if (type == HW_VC8000D && !vpu_subsys[id].submodule_hwregs[type]) + type = HW_VC8000DJ; + if (dev->client_type[id] == DWL_CLIENT_TYPE_BO_AV1_DEC) + type = HW_BIGOCEAN; + + if (id >= MAX_SUBSYS_NUM || + !vpu_subsys[id].base_addr || + core->type >= HW_CORE_MAX || + !vpu_subsys[id].submodule_hwregs[type]) + return -EINVAL; + + PDEBUG("hantrodec: submodule_iosize = %d\n", vpu_subsys[id].submodule_iosize[type]); + + ret = copy_from_user(dec_regs[id], core->regs, vpu_subsys[id].submodule_iosize[type]); + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + if (type == HW_VC8000D || type == HW_BIGOCEAN || type == HW_VC8000DJ) { + /* write all regs but the status reg[1] to hardware */ + if (reg_access_opt) { + for(i = 3; i < vpu_subsys[id].submodule_iosize[type]/4; i++) { + /* check whether register value is updated. */ + if (dec_regs[id][i] != shadow_dec_regs[id][i]) { + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id][type] + i*4)); + shadow_dec_regs[id][i] = dec_regs[id][i]; +#ifdef HANTRODEC_DEBUG + reg_wr++; +#endif + } + } + } else { + for(i = 3; i < vpu_subsys[id].submodule_iosize[type]/4; i++) { + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id][type] + i*4)); +#ifdef VALIDATE_REGS_WRITE + if (dec_regs[id][i] != ioread32((void*)(dev->hwregs[id][type] + i*4))) + pr_info("hantrodec: swreg[%ld]: read %08x != write %08x *\n", + i, ioread32((void*)(dev->hwregs[id][type] + i*4)), dec_regs[id][i]); +#endif + } +#ifdef HANTRODEC_DEBUG + reg_wr = vpu_subsys[id].submodule_iosize[type]/4 - 1; +#endif + } + + /* write swreg2 for AV1, in which bit0 is the start bit */ + iowrite32(dec_regs[id][2], (void*)(dev->hwregs[id][type] + 8)); + shadow_dec_regs[id][2] = dec_regs[id][2]; + + /* write the status register, which may start the decoder */ + iowrite32(dec_regs[id][1], (void*)(dev->hwregs[id][type] + 4)); + shadow_dec_regs[id][1] = dec_regs[id][1]; + +#ifdef HANTRODEC_DEBUG + flush_count++; + flush_regs += reg_wr; +#endif + + PDEBUG("flushed registers on core %d\n", id); + PDEBUG("%d DecFlushRegs: flushed %d/%d registers (dec_mode = %d, avg %d regs per flush)\n", + flush_count, reg_wr, flush_regs, dec_regs[id][3]>>27, flush_regs/flush_count); + } else { + /* write all regs but the status reg[1] to hardware */ + for(i = 0; i < vpu_subsys[id].submodule_iosize[type]/4; i++) { + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id][type] + i*4)); +#ifdef VALIDATE_REGS_WRITE + if (dec_regs[id][i] != ioread32((void*)(dev->hwregs[id][type] + i*4))) + pr_info("hantrodec: swreg[%ld]: read %08x != write %08x *\n", + i, ioread32((void*)(dev->hwregs[id][type] + i*4)), dec_regs[id][i]); +#endif + } + } + + return 0; +} + + +long DecWriteRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret = 0; + u32 i = core->reg_id; + u32 id = core->id; + u32 type = core->type; + + PDEBUG("hantrodec: DecWriteRegs\n"); + PDEBUG("hantrodec: id = %d, type = %d, size = %d, reg_id = %d\n", + core->id, core->type, core->size, core->reg_id); + + if (type == HW_VC8000D && !vpu_subsys[id].submodule_hwregs[type]) + type = HW_VC8000DJ; + if (dev->client_type[id] == DWL_CLIENT_TYPE_BO_AV1_DEC) + type = HW_BIGOCEAN; + + if (id >= MAX_SUBSYS_NUM || + !vpu_subsys[id].base_addr || + type >= HW_CORE_MAX || + !vpu_subsys[id].submodule_hwregs[type] || + (core->size & 0x3) || + core->reg_id * 4 + core->size > vpu_subsys[id].submodule_iosize[type]) + return -EINVAL; + + ret = copy_from_user(dec_regs[id], core->regs, core->size); + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + for (i = core->reg_id; i < core->reg_id + core->size/4; i++) { + PDEBUG("hantrodec: write %08x to reg[%d] core %d\n", dec_regs[id][i-core->reg_id], i, id); + iowrite32(dec_regs[id][i-core->reg_id], (void*)(dev->hwregs[id][type] + i*4)); + if (type == HW_VC8000D) + shadow_dec_regs[id][i] = dec_regs[id][i-core->reg_id]; + } + return 0; +} + +long DecWriteApbFilterRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret = 0; + u32 i = core->reg_id; + u32 id = core->id; + + PDEBUG("hantrodec: DecWriteApbFilterRegs\n"); + PDEBUG("hantrodec: id = %d, type = %d, size = %d, reg_id = %d\n", + core->id, core->type, core->size, core->reg_id); + + if (id >= MAX_SUBSYS_NUM || + !vpu_subsys[id].base_addr || + core->type >= HW_CORE_MAX || + !vpu_subsys[id].submodule_hwregs[core->type] || + (core->size & 0x3) || + core->reg_id * 4 + core->size > vpu_subsys[id].submodule_iosize[core->type] + 4) + return -EINVAL; + + ret = copy_from_user(apbfilter_regs[id], core->regs, core->size); + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + for (i = core->reg_id; i < core->reg_id + core->size/4; i++) { + PDEBUG("hantrodec: write %08x to reg[%d] core %d\n", dec_regs[id][i-core->reg_id], i, id); + iowrite32(apbfilter_regs[id][i-core->reg_id], (void*)(dev->apbfilter_hwregs[id][core->type] + i*4)); + } + return 0; +} + +long DecReadRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret; + u32 id = core->id; + u32 i = core->reg_id; + u32 type = core->type; + + PDEBUG("hantrodec: DecReadRegs\n"); + PDEBUG("hantrodec: id = %d, type = %d, size = %d, reg_id = %d\n", + core->id, core->type, core->size, core->reg_id); + + if (type == HW_VC8000D && !vpu_subsys[id].submodule_hwregs[type]) + type = HW_VC8000DJ; + if (dev->client_type[id] == DWL_CLIENT_TYPE_BO_AV1_DEC) + type = HW_BIGOCEAN; + + if (id >= MAX_SUBSYS_NUM || + !vpu_subsys[id].base_addr || + type >= HW_CORE_MAX || + !vpu_subsys[id].submodule_hwregs[type] || + (core->size & 0x3) || + core->reg_id * 4 + core->size > vpu_subsys[id].submodule_iosize[type]) + return -EINVAL; + + /* read specific registers from hardware */ + for (i = core->reg_id; i < core->reg_id + core->size/4; i++) { + dec_regs[id][i-core->reg_id] = ioread32((void*)(dev->hwregs[id][type] + i*4)); + PDEBUG("hantrodec: read %08x from reg[%d] core %d\n", dec_regs[id][i-core->reg_id], i, id); + if (type == HW_VC8000D) + shadow_dec_regs[id][i] = dec_regs[id][i]; + } + + /* put registers to user space*/ + ret = copy_to_user(core->regs, dec_regs[id], core->size); + if (ret) { + PDEBUG("copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + return 0; +} + +long DecRefreshRegs(hantrodec_t *dev, struct core_desc *core) +{ + long ret, i; + u32 id = core->id; + u32 type = core->type; + + PDEBUG("hantrodec: DecRefreshRegs\n"); + PDEBUG("hantrodec: id = %d, type = %d, size = %d, reg_id = %d\n", + core->id, core->type, core->size, core->reg_id); + + if (type == HW_VC8000D && !vpu_subsys[id].submodule_hwregs[type]) + type = HW_VC8000DJ; + if (dev->client_type[id] == DWL_CLIENT_TYPE_BO_AV1_DEC) + type = HW_BIGOCEAN; + + if (id >= MAX_SUBSYS_NUM || + !vpu_subsys[id].base_addr || + type >= HW_CORE_MAX || + !vpu_subsys[id].submodule_hwregs[type]) + return -EINVAL; + + PDEBUG("hantrodec: submodule_iosize = %d\n", vpu_subsys[id].submodule_iosize[type]); + + if (!reg_access_opt) { + for(i = 0; i < vpu_subsys[id].submodule_iosize[type]/4; i++) { + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id][type] + i*4)); + } + } else { + // only need to read swreg1,62(?),63,168,169 +#define REFRESH_REG(idx) i = (idx); shadow_dec_regs[id][i] = dec_regs[id][i] = ioread32((void*)(dev->hwregs[id][type] + i*4)) + REFRESH_REG(0); + REFRESH_REG(1); + REFRESH_REG(62); + REFRESH_REG(63); + REFRESH_REG(168); + REFRESH_REG(169); +#undef REFRESH_REG + } + + ret = copy_to_user(core->regs, dec_regs[id], vpu_subsys[id].submodule_iosize[type]); + if (ret) { + PDEBUG("copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + return 0; +} + +static int CheckDecIrq(hantrodec_t *dev, int id) { + unsigned long flags; + int rdy = 0; + + const u32 irq_mask = (1 << id); + + spin_lock_irqsave(&owner_lock, flags); + + if(dec_irq & irq_mask) { + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + rdy = 1; + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return rdy; +} + +long WaitDecReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + u32 id = core->id; + long ret; + + PDEBUG("wait_event_interruptible DEC[%d]\n", id); +#ifdef USE_SW_TIMEOUT + u32 status; + ret = wait_event_interruptible_timeout(dec_wait_queue, CheckDecIrq(dev, id), msecs_to_jiffies(2000)); + if(ret < 0) { + PDEBUG("DEC[%d] wait_event_interruptible interrupted\n", id); + return -ERESTARTSYS; + } else if (ret == 0) { + PDEBUG("DEC[%d] wait_event_interruptible timeout\n", id); + status = ioread32((void*)(dev->hwregs[id][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + /* check if HW is enabled */ + if(status & HANTRODEC_DEC_E) { + pr_info("hantrodec: DEC[%d] reset becuase of timeout\n", id); + + /* abort decoder */ + status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void*)(dev->hwregs[id][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + } + } +#else + ret = wait_event_interruptible(dec_wait_queue, CheckDecIrq(dev, id)); + if(ret) { + PDEBUG("DEC[%d] wait_event_interruptible interrupted\n", id); + return -ERESTARTSYS; + } +#endif + atomic_inc(&irq_tx); + + /* refresh registers */ + return DecRefreshRegs(dev, core); +} + +#if 0 +long PPFlushRegs(hantrodec_t *dev, struct core_desc *core) { + long ret = 0; + u32 id = core->id; + u32 i; + + /* copy original dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id] + HANTRO_PP_ORG_FIRST_REG, + core->regs + HANTRO_PP_ORG_FIRST_REG, + HANTRO_PP_ORG_REGS*4); + if (sizeof(void *) == 8) { + /* copy extended dec regs to kernal space*/ + ret = copy_from_user(dec_regs[id] + HANTRO_PP_EXT_FIRST_REG, + core->regs + HANTRO_PP_EXT_FIRST_REG, + HANTRO_PP_EXT_REGS*4); + } + if (ret) { + PDEBUG("copy_from_user failed, returned %li\n", ret); + return -EFAULT; + } + + /* write all regs but the status reg[1] to hardware */ + /* both original and extended regs need to be written */ + for(i = HANTRO_PP_ORG_FIRST_REG + 1; i <= HANTRO_PP_ORG_LAST_REG; i++) + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + if (sizeof(void *) == 8) { + for(i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) + iowrite32(dec_regs[id][i], (void*)(dev->hwregs[id] + i*4)); + } + /* write the stat reg, which may start the PP */ + iowrite32(dec_regs[id][HANTRO_PP_ORG_FIRST_REG], + (void*)(dev->hwregs[id] + HANTRO_PP_ORG_FIRST_REG * 4)); + + return 0; +} + +long PPRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + long i, ret; + u32 id = core->id; + if (sizeof(void *) == 8) { + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_PP_TOTAL_REGS * 4)) + return -EFAULT; + } else { + /* user has to know exactly what they are asking for */ + if(core->size != (HANTRO_PP_ORG_REGS * 4)) + return -EFAULT; + } + + /* read all registers from hardware */ + /* both original and extended regs need to be read */ + for(i = HANTRO_PP_ORG_FIRST_REG; i <= HANTRO_PP_ORG_LAST_REG; i++) + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + if (sizeof(void *) == 8) { + for(i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) + dec_regs[id][i] = ioread32((void*)(dev->hwregs[id] + i*4)); + } + /* put registers to user space*/ + /* put original registers to user space*/ + ret = copy_to_user(core->regs + HANTRO_PP_ORG_FIRST_REG, + dec_regs[id] + HANTRO_PP_ORG_FIRST_REG, + HANTRO_PP_ORG_REGS*4); + if (sizeof(void *) == 8) { + /* put extended registers to user space*/ + ret = copy_to_user(core->regs + HANTRO_PP_EXT_FIRST_REG, + dec_regs[id] + HANTRO_PP_EXT_FIRST_REG, + HANTRO_PP_EXT_REGS * 4); + } + if (ret) { + PDEBUG("copy_to_user failed, returned %li\n", ret); + return -EFAULT; + } + + return 0; +} + +static int CheckPPIrq(hantrodec_t *dev, int id) { + unsigned long flags; + int rdy = 0; + + const u32 irq_mask = (1 << id); + + spin_lock_irqsave(&owner_lock, flags); + + if(pp_irq & irq_mask) { + /* reset the wait condition(s) */ + pp_irq &= ~irq_mask; + rdy = 1; + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return rdy; +} + +long WaitPPReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core) { + u32 id = core->id; + + PDEBUG("wait_event_interruptible PP[%d]\n", id); + + if(wait_event_interruptible(pp_wait_queue, CheckPPIrq(dev, id))) { + PDEBUG("PP[%d] wait_event_interruptible interrupted\n", id); + return -ERESTARTSYS; + } + + atomic_inc(&irq_tx); + + /* refresh registers */ + return PPRefreshRegs(dev, core); +} +#endif + +static int CheckCoreIrq(hantrodec_t *dev, const struct file *filp, int *id) { + unsigned long flags; + int rdy = 0, n = 0; + + do { + u32 irq_mask = (1 << n); + + spin_lock_irqsave(&owner_lock, flags); + + if(dec_irq & irq_mask) { + if (dec_owner[n] == filp) { + /* we have an IRQ for our client */ + + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + + /* signal ready core no. for our client */ + *id = n; + + rdy = 1; + + spin_unlock_irqrestore(&owner_lock, flags); + break; + } else if(dec_owner[n] == NULL) { + /* zombie IRQ */ + pr_info("IRQ on core[%d], but no owner!!!\n", n); + + /* reset the wait condition(s) */ + dec_irq &= ~irq_mask; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + n++; /* next core */ + } while(n < dev->cores); + + return rdy; +} + +long WaitCoreReady(hantrodec_t *dev, const struct file *filp, int *id) { + long ret; + PDEBUG("wait_event_interruptible CORE\n"); +#ifdef USE_SW_TIMEOUT + u32 i, status; + ret = wait_event_interruptible_timeout(dec_wait_queue, CheckCoreIrq(dev, filp, id), msecs_to_jiffies(2000)); + if(ret < 0) { + PDEBUG("CORE wait_event_interruptible interrupted\n"); + return -ERESTARTSYS; + } else if (ret == 0) { + PDEBUG("CORE wait_event_interruptible timeout\n"); + for(i = 0; i < dev->cores; i++) { + status = ioread32((void*)(dev->hwregs[i][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + /* check if HW is enabled */ + if((status & HANTRODEC_DEC_E) && dec_owner[i] == filp) { + pr_info("hantrodec: CORE[%d] reset becuase of timeout\n", i); + *id = i; + /* abort decoder */ + status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void*)(dev->hwregs[i][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + break; + } + } + } +#else + ret = wait_event_interruptible(dec_wait_queue, CheckCoreIrq(dev, filp, id)); + if(ret) { + PDEBUG("CORE[%d] wait_event_interruptible interrupted with 0x%x\n", *id, ret); + return -ERESTARTSYS; + } +#endif + atomic_inc(&irq_tx); + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_ioctl + Description : communication method to/from the user space + + Return type : long +------------------------------------------------------------------------------*/ + +static long hantrodec_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { + int err = 0; + long tmp; + u32 i = 0; +#ifdef CLK_CFG + unsigned long flags; +#endif + +#ifdef HW_PERFORMANCE + struct timeval *end_time_arg; +#endif + + PDEBUG("ioctl cmd 0x%08x\n", cmd); + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != HANTRODEC_IOC_MAGIC && + _IOC_TYPE(cmd) != HANTRO_IOC_MMU && + _IOC_TYPE(cmd) != MEMORY_IOC_MAGIC && + _IOC_TYPE(cmd) != HANTRO_VCMD_IOC_MAGIC) + return -ENOTTY; + if ((_IOC_TYPE(cmd) == HANTRODEC_IOC_MAGIC && + _IOC_NR(cmd) > HANTRODEC_IOC_MAXNR) || + (_IOC_TYPE(cmd) == HANTRO_IOC_MMU && + _IOC_NR(cmd) > HANTRO_IOC_MMU_MAXNR) || + (_IOC_TYPE(cmd) == MEMORY_IOC_MAGIC && + _IOC_NR(cmd) > MEMORY_IOC_MAXNR) || + (_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_VCMD_IOC_MAXNR)) + return -ENOTTY; + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); +#endif + else if (_IOC_DIR(cmd) & _IOC_WRITE) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); +#endif + + if (err) + return -EFAULT; + +#ifdef CLK_CFG + spin_lock_irqsave(&clk_lock, flags); + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)&&(is_clk_on==0)) { + printk("turn on clock by user\n"); + if (clk_enable(clk_cfg)) { + spin_unlock_irqrestore(&clk_lock, flags); + return -EFAULT; + } else + is_clk_on=1; + } + spin_unlock_irqrestore(&clk_lock, flags); + mod_timer(&timer, jiffies + 10*HZ); /*the interval is 10s*/ +#endif + + switch (cmd) { + case HANTRODEC_IOC_CLI: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + disable_irq(hantrodec_data.irq[id]); + break; + } + case HANTRODEC_IOC_STI: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + enable_irq(hantrodec_data.irq[id]); + break; + } + case HANTRODEC_IOCGHWOFFSET: { + __u32 id; + __get_user(id, (__u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + + __put_user(multicorebase_actual[id], (unsigned long *) arg); + break; + } + case HANTRODEC_IOCGHWIOSIZE: { + struct regsize_desc core; + pm_runtime_resume_and_get(&hantrodec_data.pdev->dev); + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct regsize_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + return -EFAULT; + } + + if(core.id >= MAX_SUBSYS_NUM /*hantrodec_data.cores*/) { + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + return -EFAULT; + } + + if (core.type == HW_SHAPER) { + u32 asic_id; + /* Shaper is configured with l2cache. */ + if (vpu_subsys[core.id].submodule_hwregs[HW_L2CACHE]) { + asic_id = ioread32((void*)vpu_subsys[core.id].submodule_hwregs[HW_L2CACHE]); + switch ((asic_id >> 16) & 0x3) { + case 1: /* cache only */ + core.size = 0; break; + case 0: /* cache + shaper */ + case 2: /* shaper only*/ + core.size = vpu_subsys[core.id].submodule_iosize[HW_L2CACHE]; + break; + default: + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + return -EFAULT; + } + } else + core.size = 0; + } else { + core.size = vpu_subsys[core.id].submodule_iosize[core.type]; + if (core.type == HW_VC8000D && !core.size && + vpu_subsys[core.id].submodule_hwregs[HW_VC8000DJ]) { + /* If VC8000D doesn't exists, while VC8000DJ exists, return VC8000DJ. */ + core.size = vpu_subsys[core.id].submodule_iosize[HW_VC8000DJ]; + } + } + copy_to_user((u32 *) arg, &core, sizeof(struct regsize_desc)); + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + + return 0; + } + case HANTRODEC_IOC_MC_OFFSETS: { + tmp = copy_to_user((unsigned long *) arg, multicorebase_actual, sizeof(multicorebase_actual)); + if (err) { + PDEBUG("copy_to_user failed, returned %li\n", tmp); + return -EFAULT; + } + break; + } + case HANTRODEC_IOC_MC_CORES: + __put_user(hantrodec_data.cores, (unsigned int *) arg); + PDEBUG("hantrodec_data.cores=%d\n", hantrodec_data.cores); + break; + case HANTRODEC_IOCS_DEC_PUSH_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecFlushRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_DEC_WRITE_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecWriteRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_DEC_WRITE_APBFILTER_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecWriteApbFilterRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_PP_PUSH_REG: { +#if 0 + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + PPFlushRegs(&hantrodec_data, &core); +#else + return EINVAL; +#endif + } + case HANTRODEC_IOCS_DEC_PULL_REG: { + printk("%s:case HANTRODEC_IOCS_DEC_PULL_REG\n",__func__); + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + printk("%s:return DecRefreshRegs!\n",__func__); + return DecRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_DEC_READ_REG: { + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return DecReadRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCS_PP_PULL_REG: { +#if 0 + struct core_desc core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return PPRefreshRegs(&hantrodec_data, &core); +#else + return EINVAL; +#endif + } + case HANTRODEC_IOCH_DEC_RESERVE: { + u32 format = 0; + __get_user(format, (unsigned long *)arg); + PDEBUG("Reserve DEC core, format = %li\n", format); + return ReserveDecoder(&hantrodec_data, filp, format); + } + case HANTRODEC_IOCT_DEC_RELEASE: { + u32 core = 0; + __get_user(core, (unsigned long *)arg); + if(core >= hantrodec_data.cores || dec_owner[core] != filp) { + PDEBUG("bogus DEC release, core = %li\n", core); + return -EFAULT; + } + + PDEBUG("Release DEC, core = %li\n", core); + + ReleaseDecoder(&hantrodec_data, core); + + break; + } + case HANTRODEC_IOCQ_PP_RESERVE: +#if 0 + return ReservePostProcessor(&hantrodec_data, filp); +#else + return EINVAL; +#endif + case HANTRODEC_IOCT_PP_RELEASE: { +#if 0 + if(arg != 0 || pp_owner[arg] != filp) { + PDEBUG("bogus PP release %li\n", arg); + return -EFAULT; + } + + ReleasePostProcessor(&hantrodec_data, arg); + break; +#else + return EINVAL; +#endif + } + case HANTRODEC_IOCX_DEC_WAIT: { + struct core_desc core; + + /* get registers from user space */ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return WaitDecReadyAndRefreshRegs(&hantrodec_data, &core); + } + case HANTRODEC_IOCX_PP_WAIT: { +#if 0 + struct core_desc core; + + /* get registers from user space */ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_desc)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + return WaitPPReadyAndRefreshRegs(&hantrodec_data, &core); +#else + return EINVAL; +#endif + } + case HANTRODEC_IOCG_CORE_WAIT: { + int id; + tmp = WaitCoreReady(&hantrodec_data, filp, &id); + __put_user(id, (int *) arg); + return tmp; + } + case HANTRODEC_IOX_ASIC_ID: { + struct core_param core; + + /* get registers from user space*/ + tmp = copy_from_user(&core, (void*)arg, sizeof(struct core_param)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + if (core.id >= MAX_SUBSYS_NUM /*hantrodec_data.cores*/ || + ((core.type == HW_VC8000D || core.type == HW_VC8000DJ) && + !vpu_subsys[core.id].submodule_iosize[core.type == HW_VC8000D] && + !vpu_subsys[core.id].submodule_iosize[core.type == HW_VC8000DJ]) || + ((core.type != HW_VC8000D && core.type != HW_VC8000DJ) && + !vpu_subsys[core.id].submodule_iosize[core.type])) { + return -EFAULT; + } + + core.size = vpu_subsys[core.id].submodule_iosize[core.type]; + if (vpu_subsys[core.id].submodule_hwregs[core.type]) + core.asic_id = ioread32((void*)hantrodec_data.hwregs[core.id][core.type]); + else if (core.type == HW_VC8000D && + hantrodec_data.hwregs[core.id][HW_VC8000DJ]) { + core.asic_id = ioread32((void*)hantrodec_data.hwregs[core.id][HW_VC8000DJ]); + } else + core.asic_id = 0; + copy_to_user((u32 *) arg, &core, sizeof(struct core_param)); + + return 0; + } + case HANTRODEC_IOCG_CORE_ID: { + u32 format = 0; + __get_user(format, (unsigned long *)arg); + + PDEBUG("Get DEC Core_id, format = %li\n", format); + return GetDecCoreID(&hantrodec_data, filp, format); + } + case HANTRODEC_IOX_ASIC_BUILD_ID: { + u32 id, hw_id; + __get_user(id, (u32*)arg); + + if(id >= hantrodec_data.cores) { + return -EFAULT; + } + if (hantrodec_data.hwregs[id][HW_VC8000D] || + hantrodec_data.hwregs[id][HW_VC8000DJ]) { + volatile u8 *hwregs; + /* VC8000D first if it exists, otherwise VC8000DJ. */ + if (hantrodec_data.hwregs[id][HW_VC8000D]) + hwregs = hantrodec_data.hwregs[id][HW_VC8000D]; + else + hwregs = hantrodec_data.hwregs[id][HW_VC8000DJ]; + hw_id = ioread32((void*)hwregs); + if (IS_G1(hw_id >> 16) || IS_G2(hw_id >> 16) || + (IS_VC8000D(hw_id >> 16) && ((hw_id & 0xFFFF) == 0x6010))) + __put_user(hw_id, (u32 *) arg); + else { + hw_id = ioread32((void*)(hwregs + HANTRODEC_HW_BUILD_ID_OFF)); + __put_user(hw_id, (u32 *) arg); + } + } else if (hantrodec_data.hwregs[id][HW_BIGOCEAN]) { + hw_id = ioread32((void*)(hantrodec_data.hwregs[id][HW_BIGOCEAN])); + if (IS_BIGOCEAN(hw_id >> 16)) + __put_user(hw_id, (u32 *) arg); + else + return -EFAULT; + } + return 0; + } + case HANTRODEC_DEBUG_STATUS: { + pr_info("hantrodec: dec_irq = 0x%08x \n", dec_irq); + pr_info("hantrodec: pp_irq = 0x%08x \n", pp_irq); + + pr_info("hantrodec: IRQs received/sent2user = %d / %d \n", + atomic_read(&irq_rx), atomic_read(&irq_tx)); + + for (tmp = 0; tmp < hantrodec_data.cores; tmp++) { + pr_info("hantrodec: dec_core[%li] %s\n", + tmp, dec_owner[tmp] == NULL ? "FREE" : "RESERVED"); + pr_info("hantrodec: pp_core[%li] %s\n", + tmp, pp_owner[tmp] == NULL ? "FREE" : "RESERVED"); + } + return 0; + } + case HANTRODEC_IOX_SUBSYS: { + struct subsys_desc subsys = {0}; + /* TODO(min): check all the subsys */ + pm_runtime_resume_and_get(&hantrodec_data.pdev->dev); + if (vcmd) { + subsys.subsys_vcmd_num = 1; + subsys.subsys_num = subsys.subsys_vcmd_num; + } else { + subsys.subsys_num = hantrodec_data.cores; + subsys.subsys_vcmd_num = 0; + } + copy_to_user((u32 *) arg, &subsys, sizeof(struct subsys_desc)); + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + return 0; + } + case HANTRODEC_IOCX_POLL: { + hantrodec_isr(0, &hantrodec_data); + return 0; + } + case HANTRODEC_IOC_APBFILTER_CONFIG: { + struct apbfilter_cfg tmp_apbfilter; + + /* get registers from user space*/ + tmp = copy_from_user(&tmp_apbfilter, (void*)arg, sizeof(struct apbfilter_cfg)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + if(tmp_apbfilter.id >= MAX_SUBSYS_NUM || tmp_apbfilter.type >= HW_CORE_MAX) { + return -EFAULT; + } + + apbfilter_cfg[tmp_apbfilter.id][tmp_apbfilter.type].id = tmp_apbfilter.id; + apbfilter_cfg[tmp_apbfilter.id][tmp_apbfilter.type].type = tmp_apbfilter.type; + + memcpy(&tmp_apbfilter, &(apbfilter_cfg[tmp_apbfilter.id][tmp_apbfilter.type]), sizeof(struct apbfilter_cfg)); + + copy_to_user((u32 *) arg, &tmp_apbfilter, sizeof(struct apbfilter_cfg)); + + return 0; + } + case HANTRODEC_IOC_AXIFE_CONFIG: { + struct axife_cfg tmp_axife; + + /* get registers from user space*/ + tmp = copy_from_user(&tmp_axife, (void*)arg, sizeof(struct axife_cfg)); + if (tmp) { + PDEBUG("copy_from_user failed, returned %li\n", tmp); + return -EFAULT; + } + + if(tmp_axife.id >= MAX_SUBSYS_NUM) { + return -EFAULT; + } + + axife_cfg[tmp_axife.id].id = tmp_axife.id; + + memcpy(&tmp_axife, &(axife_cfg[tmp_axife.id]), sizeof(struct axife_cfg)); + + copy_to_user((u32 *) arg, &tmp_axife, sizeof(struct axife_cfg)); + + return 0; + } + default: { + if(_IOC_TYPE(cmd) == HANTRO_IOC_MMU) { + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; + for (i = 0; i < MAX_SUBSYS_NUM; i++ ) { + mmu_hwregs[i][0] = hantrodec_data.hwregs[i][HW_MMU]; + mmu_hwregs[i][1] = hantrodec_data.hwregs[i][HW_MMU_WR]; + } + pm_runtime_resume_and_get(&hantrodec_data.pdev->dev); + long retval = MMUIoctl(cmd, filp, arg, mmu_hwregs); + pm_runtime_mark_last_busy(&hantrodec_data.pdev->dev); + pm_runtime_put_autosuspend(&hantrodec_data.pdev->dev); + return retval; + } else if (_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC) { + return (hantrovcmd_ioctl(filp, cmd, arg)); + } else if (_IOC_TYPE(cmd) == MEMORY_IOC_MAGIC) { + return (allocator_ioctl(filp, cmd, arg)); + } + return -ENOTTY; + } + } + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_open + Description : open method + + Return type : int +------------------------------------------------------------------------------*/ + +static int hantrodec_open(struct inode *inode, struct file *filp) { + PDEBUG("dev opened\n"); + pr_debug("==========%s:open!============\n",__func__); + if (vcmd) + hantrovcmd_open(inode, filp); + + allocator_open(inode, filp); + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_release + Description : Release driver + + Return type : int +------------------------------------------------------------------------------*/ + +static int hantrodec_release(struct inode *inode, struct file *filp) { + int n; + hantrodec_t *dev = &hantrodec_data; + + PDEBUG("closing ...\n"); + + if (vcmd) { + hantrovcmd_release(inode, filp); + allocator_release(inode, filp); + return 0; + } + + for(n = 0; n < dev->cores; n++) { + if(dec_owner[n] == filp) { + PDEBUG("releasing dec core %i lock\n", n); + ReleaseDecoder(dev, n); + } + } + + for(n = 0; n < 1; n++) { + if(pp_owner[n] == filp) { + PDEBUG("releasing pp core %i lock\n", n); + ReleasePostProcessor(dev, n); + } + } + + MMURelease(filp, hantrodec_data.hwregs[0][HW_MMU]); + + allocator_release(inode, filp); + + PDEBUG("closed\n"); + return 0; +} + +#ifdef CLK_CFG +void hantrodec_disable_clk(unsigned long value) { + unsigned long flags; + /*entering this function means decoder is idle over expiry.So disable clk*/ + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)) { + spin_lock_irqsave(&clk_lock, flags); + if (is_clk_on==1) { + clk_disable(clk_cfg); + is_clk_on = 0; + pr_info("turned off hantrodec clk\n"); + } + spin_unlock_irqrestore(&clk_lock, flags); + } +} +#endif + +static int mmap_cmdbuf_mem(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + /* Does it even fit in phys_addr_t? */ + if (offset >> PAGE_SHIFT != vma->vm_pgoff) + return -EINVAL; + + /* It's illegal to wrap around the end of the physical address space. */ + if (offset + (phys_addr_t)size - 1 < offset) + return -EINVAL; + + + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, + size, + vma->vm_page_prot); + + /* Remap-pfn-range will mark the range VM_IO */ + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + size, + vma->vm_page_prot)) { + return -EAGAIN; + } + + return 0; +} + +static int mmap_mem(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + if (hantro_cmdbuf_range(offset,size)){ + return mmap_cmdbuf_mem(file,vma); + }else{ + return allocator_mmap(file,vma); + } +} + +/* VFS methods */ +static struct file_operations hantrodec_fops = { + .owner = THIS_MODULE, + .open = hantrodec_open, + .release = hantrodec_release, + .unlocked_ioctl = hantrodec_ioctl, + .mmap = mmap_mem, + .fasync = NULL +}; + +static int PcieInit(void) { + int i; + + gDev = pci_get_device(PCI_VENDOR_ID_HANTRO, PCI_DEVICE_ID_HANTRO_PCI, gDev); + if (NULL == gDev) { + pr_info("Init: Hardware not found.\n"); + goto out; + } + + if (0 > pci_enable_device(gDev)) { + pr_info("PcieInit: Device not enabled.\n"); + goto out; + } + + gBaseHdwr = pci_resource_start (gDev, PCI_CONTROL_BAR); + if (0 == gBaseHdwr) { + pr_info("PcieInit: Base Address not set.\n"); + goto out_pci_disable_device; + } + pr_info("Base hw val 0x%X\n", (unsigned int)gBaseHdwr); + + gBaseLen = pci_resource_len (gDev, PCI_CONTROL_BAR); + pr_info("Base hw len 0x%X\n", (unsigned int)gBaseLen); + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (vpu_subsys[i].base_addr) { + vpu_subsys[i].base_addr += gBaseHdwr; + multicorebase[i] += gBaseHdwr; + } + } + + gBaseDDRHw = pci_resource_start (gDev, PCI_DDR_BAR); + if (0 == gBaseDDRHw) { + pr_info("PcieInit: Base Address not set.\n"); + goto out_pci_disable_device; + } + pr_info("Base memory val 0x%llx\n", (unsigned int)gBaseDDRHw); + + gBaseLen = pci_resource_len (gDev, PCI_DDR_BAR); + pr_info("Base memory len 0x%x\n", (unsigned int)gBaseLen); + + return 0; + +out_pci_disable_device: + pci_disable_device(gDev); + +out: + return -1; +} + +static void dump_vpu_subsys(struct subsys_config *cfg) +{ + int i; + pr_info("lucz: dumping subsys_config[0]\n"); + pr_info(" base_addr=0x%llx\n", cfg->base_addr); + if (cfg->base_addr == 0) { + pr_info(" base_addr=0, not dump any more\n"); + return; + } + pr_info(" irq=%d\n", cfg->irq); + pr_info(" subsys_type=%u\n", cfg->subsys_type); + pr_info(" submodule_offset="); + for (i = 0; i < HW_CORE_MAX; i++) { + pr_info(" 0x%x,", cfg->submodule_offset[i]); + } + pr_info("\n"); + pr_info(" submodule_iosize="); + for (i = 0; i < HW_CORE_MAX; i++) { + pr_info(" %d,", cfg->submodule_iosize[i]); + } + pr_info("\n"); + pr_info(" submodule_hwregs="); + for (i = 0; i < HW_CORE_MAX; i++) { + pr_info(" %p,", cfg->submodule_hwregs[i]); + } + pr_info("\n"); + pr_info(" has_apbfilter="); + for (i = 0; i < HW_CORE_MAX; i++) { + pr_info(" %d,", cfg->has_apbfilter[i]); + } + pr_info("\n"); +} + +static ssize_t decoder_config_write(struct file *filp, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + hantrodec_t *dev = &hantrodec_data; + unsigned long value; + int ret; + + if (count > VC8000D_MAX_CONFIG_LEN) + count = VC8000D_MAX_CONFIG_LEN; + else if (count <= 2) + return 0; + + ret = copy_from_user(dev->config_buf, userbuf, count); + if (ret) { + ret = -EFAULT; + goto out; + } + + //pr_info("hantrodec config: %s\n", dev->config_buf); + switch (dev->config_buf[0]) { + case 'd': + value = simple_strtoul(&(dev->config_buf[1]), NULL, 10); + pm_runtime_set_autosuspend_delay(&dev->pdev->dev, value); + pr_info("Set pm runtime auto suspend delay to %ldms\n", value); + break; + default: + printk(KERN_WARNING "Unsupported config!\n"); + } + +out: + return ret < 0 ? ret : count; +} + +static ssize_t decoder_config_read(struct file *filp, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + hantrodec_t *dev = &hantrodec_data; + memset(dev->config_buf, 0, VC8000D_MAX_CONFIG_LEN); + return 0; +} + +static const struct file_operations decoder_debug_ops = { + .write = decoder_config_write, + .read = decoder_config_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static int decoder_add_debugfs(struct platform_device *pdev) +{ + root_debugfs_dir = debugfs_create_dir("vc8000d",NULL); + if (!root_debugfs_dir) { + dev_err(&pdev->dev, "Failed to create vc8000d debugfs\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "Create vc8000d debugfs.\n"); + + debugfs_create_file("config", 0600, root_debugfs_dir, + &hantrodec_data, &decoder_debug_ops); + return 0; +} + +/*------------------------------------------------------------ +platform register + +------------------------------------------------------------*/ + +static const struct of_device_id isp_of_match[] = { + { .compatible = "thead,light-vc8000d", }, + { /* sentinel */ }, +}; + + +static int check_power_domain(void) +{ + struct device_node *dn = NULL; + struct property *info = NULL; + dn = of_find_node_by_name(NULL, "vdec"); + if (dn != NULL) + info = of_find_property(dn, "power-domains", NULL); + pr_debug("%s, %d: power gating is %s\n", __func__, __LINE__, + (info == NULL) ? "disabled" : "enabled"); + return (info == NULL) ? 0 : 1; +} + +static int decoder_runtime_suspend(struct device *dev) +{ + hantrodec_t *decdev = &hantrodec_data; + + pr_debug("%s, %d: Disable clock\n", __func__, __LINE__); + + clk_disable_unprepare(decdev->cclk); + clk_disable_unprepare(decdev->aclk); + clk_disable_unprepare(decdev->pclk); + + return 0; +} + +static int decoder_runtime_resume(struct device *dev) +{ + hantrodec_t *decdev = &hantrodec_data; + int ret; + + ret = clk_prepare_enable(decdev->cclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable core clock\n"); + return ret; + } + + ret = clk_prepare_enable(decdev->aclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable axi clock\n"); + clk_disable_unprepare(decdev->cclk); + return ret; + } + + ret = clk_prepare_enable(decdev->pclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable apb clock\n"); + clk_disable_unprepare(decdev->cclk); + clk_disable_unprepare(decdev->aclk); + return ret; + } + + if (hantrodec_data.has_power_domains) { + if (hantrodec_data.hwregs[0][HW_MMU]) { + int i; + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; + for (i = 0; i < MAX_SUBSYS_NUM; i++ ) { + mmu_hwregs[i][0] = hantrodec_data.hwregs[i][HW_MMU]; + mmu_hwregs[i][1] = hantrodec_data.hwregs[i][HW_MMU_WR]; + } + MMURestore(mmu_hwregs); + } + hantrovcmd_reset(); + } + + pr_debug("%s, %d: Enabled clock\n", __func__, __LINE__); + + return 0; +} + +static int decoder_hantrodec_probe(struct platform_device *pdev) +{ + printk("enter %s\n",__func__); + printk("pcie=%d\n",pcie); + int result, i; + struct resource *mem; + //struct decoder_driver_device *pdriver_dev; + enum MMUStatus status = 0; + enum MMUStatus mmu_status = MMU_STATUS_FALSE; + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; + //pdriver_dev = devm_kzalloc(&pdev->dev,sizeof(struct decoder_driver_device),GFP_KERNEL); + // if(pdriver_dev == NULL) + //{ +// pr_err("%s:alloc struct deocder_driver_device error!\n",__func__); +// return -ENOMEM; + /// } + + //pdriver_dev->hantrodec_class = class_create(THIS_MODULE,"hantrodec"); + printk("%s:init variable is ok!\n",__func__); + mem = platform_get_resource(pdev,IORESOURCE_MEM,0); + printk("%s:get resource is ok!\n",__func__); + //devm_ioremap_resource(&pdev->dev,mem); + if(mem->start) + base_port = mem->start; + else + printk("%s:mem->start is not exist!\n",__func__); + printk("%s:start get irq!\n",__func__); + + + PDEBUG("module init\n"); + + CheckSubsysCoreArray(vpu_subsys, &vcmd); + irq[0] = platform_get_irq(pdev,0); + printk("%s:get irq!\n",__func__); + printk("%s:base_port=0x%llx,irq=%d\n",__func__,base_port,irq[0]); + printk("%s:pcie=%d\n",__func__,pcie); + + if (pcie) { + result = PcieInit(); + if(result) + goto err; + } + + pr_info("hantrodec: dec/pp kernel module. \n"); + + /* If base_port is set when insmod, use that for single core legacy mode. */ + if (base_port != -1) { + multicorebase[0] = base_port; + if (pcie) + multicorebase[0] += HANTRO_REG_OFFSET0; + elements = 1; + vpu_subsys[0].base_addr = base_port; + pr_info("hantrodec: Init single core at 0x%08lx IRQ=%i\n", + multicorebase[0], irq[0]); + } else { + pr_info("hantrodec: Init multi core[0] at 0x%16lx\n" + " core[1] at 0x%16lx\n" + " core[2] at 0x%16lx\n" + " core[3] at 0x%16lx\n" + " IRQ_0=%i\n" + " IRQ_1=%i\n", + multicorebase[0], multicorebase[1], + multicorebase[2], multicorebase[3], + irq[0],irq[1]); + } + + hantrodec_data.pdev = pdev; + hantrodec_data.cores = 0; + + hantrodec_data.iosize[0] = DEC_IO_SIZE_0; + hantrodec_data.irq[0] = irq[0]; + hantrodec_data.iosize[1] = DEC_IO_SIZE_1; + hantrodec_data.irq[1] = irq[1]; + + //extern void dump_core_array(void); + //dump_vpu_subsys(&(vpu_subsys[0])); + //dump_core_array(); + pr_info("hantrodec_data.irq=%d\n", + hantrodec_data.irq[0]); + + for(i=0; i< HXDEC_MAX_CORES; i++) { + int j; + for (j = 0; j < HW_CORE_MAX; j++) + hantrodec_data.hwregs[i][j] = 0; + /* If user gave less core bases that we have by default, + * invalidate default bases + */ + if(elements && i>=elements) { + multicorebase[i] = 0; + } + } + + hantrodec_data.async_queue_dec = NULL; + hantrodec_data.async_queue_pp = NULL; + + hantrodec_data.has_power_domains = check_power_domain(); + + hantrodec_data.cclk = devm_clk_get(&pdev->dev, "cclk"); + if (IS_ERR(hantrodec_data.cclk)) { + dev_err(&pdev->dev, "failed to get core clock\n"); + goto err; + } + + hantrodec_data.aclk = devm_clk_get(&pdev->dev, "aclk"); + if (IS_ERR(hantrodec_data.aclk)) { + dev_err(&pdev->dev, "failed to get axi clock\n"); + goto err; + } + + hantrodec_data.pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(hantrodec_data.pclk)) { + dev_err(&pdev->dev, "failed to get apb clock\n"); + goto err; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, VC8000D_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + if (decoder_runtime_resume(&pdev->dev)) + { + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + } + } + pm_runtime_resume_and_get(&pdev->dev); + + if (hantrodec_major == 0) + { + result = alloc_chrdev_region(&hantrodec_devt, 0, 1, "hantrodec"); + if (result != 0) + { + printk(KERN_ERR "%s: alloc_chrdev_region error\n", __func__); + goto err; + } + hantrodec_major = MAJOR(hantrodec_devt); + hantrodec_minor = MINOR(hantrodec_devt); + } + else + { + hantrodec_devt = MKDEV(hantrodec_major, hantrodec_minor); + result = register_chrdev_region(hantrodec_devt, 1, "hantrodec"); + if (result) + { + printk(KERN_ERR "%s: register_chrdev_region error\n", __func__); + goto err; + } + } + + hantrodec_class = class_create(THIS_MODULE, "hantrodec"); + if (IS_ERR(hantrodec_class)) + { + printk(KERN_ERR "%s, %d: class_create error!\n", __func__, __LINE__); + goto err; + } + hantrodec_devt = MKDEV(hantrodec_major, hantrodec_minor); + + cdev_init(&hantrodec_cdev, &hantrodec_fops); + result = cdev_add(&hantrodec_cdev, hantrodec_devt, 1); + if ( result ) + { + printk(KERN_ERR "%s, %d: cdev_add error!\n", __func__, __LINE__); + goto err; + } + + device_create(hantrodec_class, NULL, hantrodec_devt, + NULL, "hantrodec"); + +#ifdef CLK_CFG + /* first get clk instance pointer */ + clk_cfg = clk_get(NULL, CLK_ID); + if (!clk_cfg||IS_ERR(clk_cfg)) { + printk("get handrodec clk failed!\n"); + goto err; + } + + /* prepare and enable clk */ + if(clk_prepare_enable(clk_cfg)) { + printk("try to enable handrodec clk failed!\n"); + goto err; + } + is_clk_on = 1; + + /*init a timer to disable clk*/ + init_timer(&timer); + timer.function = &hantrodec_disable_clk; + timer.expires = jiffies + 100*HZ; //the expires time is 100s + add_timer(&timer); +#endif + + result = ReserveIO(); + if(result < 0) { + goto err; + } + + for (i = 0; i < hantrodec_data.cores; i++) { + AXIFEEnable(hantrodec_data.hwregs[i][HW_AXIFE]); + } + + /* MMU only initial once No matter how many MMU we have */ + if (hantrodec_data.hwregs[0][HW_MMU]) { + status = MMUInit(hantrodec_data.hwregs[0][HW_MMU]); + if(status == MMU_STATUS_NOT_FOUND) + pr_info("MMU does not exist!\n"); + else if(status != MMU_STATUS_OK) + goto err; + else + pr_info("MMU detected!\n"); + + for (i = 0; i < MAX_SUBSYS_NUM; i++ ) { + mmu_hwregs[i][0] = hantrodec_data.hwregs[i][HW_MMU]; + mmu_hwregs[i][1] = hantrodec_data.hwregs[i][HW_MMU_WR]; + } + mmu_status = MMUEnable(mmu_hwregs); + } + + allocator_init(&pdev->dev); + + decoder_add_debugfs(pdev); + + if (vcmd) { + /* unmap and release mem region for VCMD, since it will be mapped and + reserved again in hantro_vcmd.c*/ + for (i = 0; i < hantrodec_data.cores; i++) { + if (hantrodec_data.hwregs[i][HW_VCMD]) { + iounmap((void *)hantrodec_data.hwregs[i][HW_VCMD]); + release_mem_region(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[HW_VCMD], + vpu_subsys[i].submodule_iosize[HW_VCMD]); + hantrodec_data.hwregs[i][HW_VCMD] = 0; + } + } + result = hantrovcmd_init(pdev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + if (result) return result; + + pr_info("PM runtime was enable\n"); + + return 0; + } + + memset(dec_owner, 0, sizeof(dec_owner)); + memset(pp_owner, 0, sizeof(pp_owner)); + + sema_init(&dec_core_sem, hantrodec_data.cores); + sema_init(&pp_core_sem, 1); + + /* read configuration fo all cores */ + ReadCoreConfig(&hantrodec_data); + + /* reset hardware */ + ResetAsic(&hantrodec_data); + + /* register irq for each core */ + if(irq[0] > 0) { + result = request_irq(irq[0], hantrodec_isr, +//#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) +// SA_INTERRUPT | SA_SHIRQ, +//#else +// IRQF_SHARED, +//#endif + IRQF_TRIGGER_RISING, + "hantrodec", (void *) &hantrodec_data); + + if(result != 0) { + if(result == -EINVAL) { + printk(KERN_ERR "hantrodec: Bad irq number or handler\n"); + } else if(result == -EBUSY) { + printk(KERN_ERR "hantrodec: IRQ <%d> busy, change your config\n", + hantrodec_data.irq[0]); + } + + ReleaseIO(); + goto err; + } + } else { + pr_info("hantrodec: IRQ not in use!\n"); + } + + if(irq[1] > 0) { + result = request_irq(irq[1], hantrodec_isr, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) + SA_INTERRUPT | SA_SHIRQ, +#else + IRQF_SHARED, +#endif + "hantrodec", (void *) &hantrodec_data); + + if(result != 0) { + if(result == -EINVAL) { + printk(KERN_ERR "hantrodec: Bad irq number or handler\n"); + } else if(result == -EBUSY) { + printk(KERN_ERR "hantrodec: IRQ <%d> busy, change your config\n", + hantrodec_data.irq[1]); + } + + ReleaseIO(); + goto err; + } + } else { + pr_info("hantrodec: IRQ not in use!\n"); + } + + for (i = 0; i < hantrodec_data.cores; i++) { + volatile u8 *hwregs = hantrodec_data.hwregs[i][HW_VC8000D]; + if (hwregs) { + pr_info("hantrodec: VC8000D [%d] has build id 0x%08x\n", + i, ioread32((void*)(hwregs + HANTRODEC_HWBUILD_ID_OFF))); + } + } + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pr_info("hantrodec: module inserted. Major = %d\n", hantrodec_major); + + /* Please call the TEE functions to set VC8000D DRM relative registers here */ + + return 0; + +err: + if (root_debugfs_dir) { + debugfs_remove_recursive(root_debugfs_dir); + root_debugfs_dir = NULL; + } + ReleaseIO(); + pr_info("hantrodec: module not inserted\n"); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + unregister_chrdev_region(hantrodec_devt, 1); + return result; +} + + +static int decoder_hantrodec_remove(struct platform_device *pdev) +{ + if (root_debugfs_dir) { + debugfs_remove_recursive(root_debugfs_dir); + root_debugfs_dir = NULL; + } + pm_runtime_resume_and_get(&pdev->dev); + /* When vcmd is true, irq free in hantrovcmd_cleanup! + When vcmd is flase, it is not need because in line 2528 freed */ + #if 0 + if(!vcmd){ + if(irq[0] > 0) + { + free_irq(irq[0],(void *) &hantrodec_data); + } + if(irq[1] > 0) + { + free_irq(irq[1],(void *) &hantrodec_data); + } + } + #endif + hantrodec_t *dev = &hantrodec_data; + int i, n =0; + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; + + for (i = 0; i < MAX_SUBSYS_NUM; i++ ) { + mmu_hwregs[i][0] = dev->hwregs[i][HW_MMU]; + mmu_hwregs[i][1] = dev->hwregs[i][HW_MMU_WR]; + } + if (dev->hwregs[0][HW_MMU] || dev->hwregs[1][HW_MMU] || + dev->hwregs[2][HW_MMU] || dev->hwregs[3][HW_MMU]) + MMUCleanup(mmu_hwregs); + + if (vcmd) { + hantrovcmd_cleanup(pdev); + } else { + /* reset hardware */ + ResetAsic(dev); + + /* free the IRQ */ + for (n = 0; n < dev->cores; n++) { + if(dev->irq[n] != -1) { + free_irq(dev->irq[n], (void *) dev); + } + } + } + ReleaseIO(); + +#ifdef CLK_CFG + if (clk_cfg!=NULL && !IS_ERR(clk_cfg)) { + clk_disable_unprepare(clk_cfg); + is_clk_on = 0; + printk("turned off hantrodec clk\n"); + } + + /*delete timer*/ + del_timer(&timer); +#endif + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + decoder_runtime_suspend(&pdev->dev); + + cdev_del(&hantrodec_cdev); + device_destroy(hantrodec_class, hantrodec_devt); + unregister_chrdev_region(hantrodec_devt, 1); + class_destroy(hantrodec_class); + + pr_info("hantrodec: module removed\n"); + return 0; +} + + +static const struct dev_pm_ops decoder_runtime_pm_ops = { + SET_RUNTIME_PM_OPS(decoder_runtime_suspend, decoder_runtime_resume, NULL) +}; + +static struct platform_driver decoder_hantrodec_driver = { + .probe = decoder_hantrodec_probe, + .remove = decoder_hantrodec_remove, + .driver = { + .name = "decoder_hantrodec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(isp_of_match), + .pm = &decoder_runtime_pm_ops, + } +}; + + +/*------------------------------------------------------------------------------ + Function name : hantrodec_init + Description : Initialize the driver + + Return type : int +------------------------------------------------------------------------------*/ + +int __init hantrodec_init(void) { + int ret = 0; + printk("enter %s\n",__func__); + ret = platform_driver_register(&decoder_hantrodec_driver); + if(ret) + { + pr_err("register platform driver failed!\n"); + } + return ret; +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_cleanup + Description : clean up + + Return type : int +------------------------------------------------------------------------------*/ + +void __exit hantrodec_cleanup(void) { + printk("enter %s\n",__func__); + platform_driver_unregister(&decoder_hantrodec_driver); + return; +} + +/*------------------------------------------------------------------------------ + Function name : CheckHwId + Return type : int +------------------------------------------------------------------------------*/ +static int CheckHwId(hantrodec_t * dev) { + int hwid; + int i, j; + size_t num_hw = sizeof(DecHwId) / sizeof(*DecHwId); + + int found = 0; + + for (i = 0; i < dev->cores; i++) { + for (j = 0; j < HW_CORE_MAX; j++) { + if ((j == HW_VC8000D || j == HW_BIGOCEAN || j == HW_VC8000DJ) && + dev->hwregs[i][j] != NULL) { + hwid = readl(dev->hwregs[i][j]); + pr_info("hantrodec: core %d HW ID=0x%08x\n", i, hwid); + hwid = (hwid >> 16) & 0xFFFF; /* product version only */ + while (num_hw--) { + if (hwid == DecHwId[num_hw]) { + pr_info("hantrodec: Supported HW found at 0x%16lx\n", + vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j]); + found++; + dev->hw_id[i][j] = hwid; + break; + } + } + if (!found) { + pr_info("hantrodec: Unknown HW found at 0x%16lx\n", + multicorebase_actual[i]); + return 0; + } + found = 0; + num_hw = sizeof(DecHwId) / sizeof(*DecHwId); + } + } + } + + return 1; +} + +/*------------------------------------------------------------------------------ + Function name : ReserveIO + Description : IO reserve + + Return type : int +------------------------------------------------------------------------------*/ +static int ReserveIO(void) { + int i, j; + long int hwid; + u32 axife_config; + + memcpy(multicorebase_actual, multicorebase, HXDEC_MAX_CORES * sizeof(unsigned long)); + memcpy((unsigned int*)(hantrodec_data.iosize), iosize, HXDEC_MAX_CORES * sizeof(unsigned int)); + memcpy((unsigned int*)(hantrodec_data.irq), irq, HXDEC_MAX_CORES * sizeof(int)); + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (!vpu_subsys[i].base_addr) continue; + + for (j = 0; j < HW_CORE_MAX; j++) { + if (vpu_subsys[i].submodule_iosize[j]) { + pr_info("hantrodec: base=0x%16lx, iosize=%d\n", + vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j]); + + if (!request_mem_region(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j], + "hantrodec0")) { + pr_info("hantrodec: failed to reserve HW %d regs\n", j); + return -EBUSY; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)) + vpu_subsys[i].submodule_hwregs[j] = + hantrodec_data.hwregs[i][j] = + (volatile u8 *) ioremap_nocache(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j]); +#else + vpu_subsys[i].submodule_hwregs[j] = + hantrodec_data.hwregs[i][j] = + (volatile u8 *) ioremap(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j]); +#endif + + if (hantrodec_data.hwregs[i][j] == NULL) { + pr_info("hantrodec: failed to ioremap HW %d regs\n", j); + release_mem_region(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j]); + return -EBUSY; + } else { + if (vpu_subsys[i].has_apbfilter[j]) { + apbfilter_cfg[i][j].has_apbfilter = 1; + hwid = ioread32((void*)(hantrodec_data.hwregs[i][HW_VC8000D])); + if (IS_BIGOCEAN(hwid & 0xFFFF)) { + if (j == HW_BIGOCEAN) { + apbfilter_cfg[i][j].nbr_mask_regs = AV1_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = AV1_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = AV1_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = AV1_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + if (j == HW_AXIFE) { + apbfilter_cfg[i][j].nbr_mask_regs = AXIFE_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = AXIFE_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = AXIFE_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = AXIFE_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + } else { + hwid = ioread32((void*)(hantrodec_data.hwregs[i][HW_VC8000D] + HANTRODEC_HW_BUILD_ID_OFF)); + if (hwid == 0x1F58) { + if (j == HW_VC8000D) { + apbfilter_cfg[i][j].nbr_mask_regs = VC8000D_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = VC8000D_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = VC8000D_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = VC8000D_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + if (j == HW_AXIFE) { + apbfilter_cfg[i][j].nbr_mask_regs = AXIFE_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = AXIFE_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = AXIFE_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = AXIFE_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + } else if (hwid == 0x1F59) { + if (j == HW_VC8000DJ) { + apbfilter_cfg[i][j].nbr_mask_regs = VC8000DJ_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = VC8000DJ_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = VC8000DJ_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = VC8000DJ_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + if (j == HW_AXIFE) { + apbfilter_cfg[i][j].nbr_mask_regs = AXIFE_NUM_MASK_REG; + apbfilter_cfg[i][j].num_mode = AXIFE_NUM_MODE; + apbfilter_cfg[i][j].mask_reg_offset = AXIFE_MASK_REG_OFFSET; + apbfilter_cfg[i][j].mask_bits_per_reg = AXIFE_MASK_BITS_PER_REG; + apbfilter_cfg[i][j].page_sel_addr = apbfilter_cfg[i][j].mask_reg_offset + apbfilter_cfg[i][j].nbr_mask_regs * 4; + } + } else { + pr_info("hantrodec: furture APBFILTER can read those configure parameters from REG\n"); + } + } + hantrodec_data.apbfilter_hwregs[i][j] = hantrodec_data.hwregs[i][j] + apbfilter_cfg[i][j].mask_reg_offset; + } else { + apbfilter_cfg[i][j].has_apbfilter = 0; + } + + if (j == HW_AXIFE) { + hwid = ioread32((void*)(hantrodec_data.hwregs[i][j] + HANTRODEC_HW_BUILD_ID_OFF)); + axife_config = ioread32((void*)(hantrodec_data.hwregs[i][j])); + axife_cfg[i].axi_rd_chn_num = axife_config & 0x7F; + axife_cfg[i].axi_wr_chn_num = (axife_config >> 7) & 0x7F; + axife_cfg[i].axi_rd_burst_length = (axife_config >> 14) & 0x1F; + axife_cfg[i].axi_wr_burst_length = (axife_config >> 22) & 0x1F; + axife_cfg[i].fe_mode = 0; /*need to read from reg in furture*/ + if (hwid == 0x1F66) { + axife_cfg[i].fe_mode = 1; + } + } + } + config.its_main_core_id[i] = -1; + config.its_aux_core_id[i] = -1; + + pr_info("hantrodec: HW %d reg[0]=0x%08x\n", j, readl(hantrodec_data.hwregs[i][j])); + +#ifdef SUPPORT_2ND_PIPELINES + if (j != HW_VC8000D) continue; + hwid = ((readl(hantrodec_data.hwregs[i][HW_VC8000D])) >> 16) & 0xFFFF; /* product version only */ + + if (IS_VC8000D(hwid)) { + u32 reg; + /*TODO(min): DO NOT support 2nd pipeline. */ + reg = readl(hantrodec_data.hwregs[i][HW_VC8000D] + HANTRODEC_SYNTH_CFG_2_OFF); + if (((reg >> DWL_H264_PIPELINE_E) & 0x01U) || ((reg >> DWL_JPEG_PIPELINE_E) & 0x01U)) { + i++; + config.its_aux_core_id[i-1] = i; + config.its_main_core_id[i] = i-1; + config.its_aux_core_id[i] = -1; + multicorebase_actual[i] = multicorebase_actual[i-1] + 0x800; + hantrodec_data.iosize[i] = hantrodec_data.iosize[i-1]; + memcpy(multicorebase_actual+i+1, multicorebase+i, + (HXDEC_MAX_CORES - i - 1) * sizeof(unsigned long)); + memcpy((unsigned int*)hantrodec_data.iosize+i+1, iosize+i, + (HXDEC_MAX_CORES - i - 1) * sizeof(unsigned int)); + if (!request_mem_region(multicorebase_actual[i], hantrodec_data.iosize[i], + "hantrodec0")) { + pr_info("hantrodec: failed to reserve HW regs\n"); + return -EBUSY; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)) + hantrodec_data.hwregs[i][HW_VC8000D] = (volatile u8 *) ioremap_nocache(multicorebase_actual[i], + hantrodec_data.iosize[i]); +#else + hantrodec_data.hwregs[i][HW_VC8000D] = (volatile u8 *) ioremap(multicorebase_actual[i], + hantrodec_data.iosize[i]); +#endif + + if (hantrodec_data.hwregs[i][HW_VC8000D] == NULL ) { + pr_info("hantrodec: failed to ioremap HW regs\n"); + ReleaseIO(); + return -EBUSY; + } + hantrodec_data.cores++; + } + } +#endif + } else { + hantrodec_data.hwregs[i][j] = NULL; + } + } + hantrodec_data.cores++; + } + + /* check for correct HW */ + if (!CheckHwId(&hantrodec_data)) { + ReleaseIO(); + return -EBUSY; + } + + return 0; +} + +/*------------------------------------------------------------------------------ + Function name : releaseIO + Description : release + + Return type : void +------------------------------------------------------------------------------*/ + +static void ReleaseIO(void) { + int i, j; + for (i = 0; i < hantrodec_data.cores; i++) { + for (j = 0; j < HW_CORE_MAX; j++) { + if (hantrodec_data.hwregs[i][j]) { + iounmap((void *) hantrodec_data.hwregs[i][j]); + release_mem_region(vpu_subsys[i].base_addr + vpu_subsys[i].submodule_offset[j], + vpu_subsys[i].submodule_iosize[j]); + hantrodec_data.hwregs[i][j] = 0; + } + } + } +} + +/*------------------------------------------------------------------------------ + Function name : hantrodec_isr + Description : interrupt handler + + Return type : irqreturn_t +------------------------------------------------------------------------------*/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +irqreturn_t hantrodec_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +irqreturn_t hantrodec_isr(int irq, void *dev_id) +#endif +{ + printk("%s:start!\n",__func__); + unsigned long flags; + unsigned int handled = 0; + int i; + volatile u8 *hwregs; + + hantrodec_t *dev = (hantrodec_t *) dev_id; + u32 irq_status_dec; + + spin_lock_irqsave(&owner_lock, flags); + + for(i=0; icores; i++) { + volatile u8 *hwregs = dev->hwregs[i][HW_VC8000D]; + + /* interrupt status register read */ + irq_status_dec = ioread32((void*)(hwregs + HANTRODEC_IRQ_STAT_DEC_OFF)); + + if(irq_status_dec & HANTRODEC_DEC_IRQ) { + /* clear dec IRQ */ + irq_status_dec &= (~HANTRODEC_DEC_IRQ); + iowrite32(irq_status_dec, (void*)(hwregs + HANTRODEC_IRQ_STAT_DEC_OFF)); + + PDEBUG("decoder IRQ received! core %d\n", i); + + atomic_inc(&irq_rx); + + dec_irq |= (1 << i); + + wake_up_interruptible_all(&dec_wait_queue); + handled++; + } + } + + spin_unlock_irqrestore(&owner_lock, flags); + + if(!handled) { + PDEBUG("IRQ received, but not hantrodec's!\n"); + } + + (void)hwregs; + printk("%s:end!\n",__func__); + return IRQ_RETVAL(handled); +} + +/*------------------------------------------------------------------------------ + Function name : ResetAsic + Description : reset asic (only VC8000D supports reset) + + Return type : +------------------------------------------------------------------------------*/ +void ResetAsic(hantrodec_t * dev) { + int i, j; + u32 status; + + for (j = 0; j < dev->cores; j++) { + if (!dev->hwregs[j][HW_VC8000D]) continue; + + status = ioread32((void*)(dev->hwregs[j][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + + if( status & HANTRODEC_DEC_E) { + /* abort with IRQ disabled */ + status = HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; + iowrite32(status, (void*)(dev->hwregs[j][HW_VC8000D] + HANTRODEC_IRQ_STAT_DEC_OFF)); + } + + if (IS_G1(dev->hw_id[j][HW_VC8000D])) + /* reset PP */ + iowrite32(0, (void*)(dev->hwregs[j][HW_VC8000D] + HANTRO_IRQ_STAT_PP_OFF)); + + for (i = 4; i < dev->iosize[j]; i += 4) { + iowrite32(0, (void*)(dev->hwregs[j][HW_VC8000D] + i)); + } + } +} + +/*------------------------------------------------------------------------------ + Function name : dump_regs + Description : Dump registers + + Return type : +------------------------------------------------------------------------------*/ +#ifdef HANTRODEC_DEBUG +void dump_regs(hantrodec_t *dev) { + int i,c; + + PDEBUG("Reg Dump Start\n"); + for(c = 0; c < dev->cores; c++) { + for(i = 0; i < dev->iosize[c]; i += 4*4) { + PDEBUG("\toffset %04X: %08X %08X %08X %08X\n", i, + ioread32(dev->hwregs[c][HW_VC8000D] + i), + ioread32(dev->hwregs[c][HW_VC8000D] + i + 4), + ioread32(dev->hwregs[c][HW_VC8000D] + i + 16), + ioread32(dev->hwregs[c][HW_VC8000D] + i + 24)); + } + } + PDEBUG("Reg Dump End\n"); +} +#endif + + +module_init( hantrodec_init); +module_exit( hantrodec_cleanup); + +/* module description */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("VeriSilicon Microelectronics "); +MODULE_DESCRIPTION("driver module for Hantro video decoder VC8000D"); + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_mmu.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_mmu.c new file mode 100644 index 000000000..bef8968aa --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_mmu.c @@ -0,0 +1,1870 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)) +#include +#else +#include +#endif +#include +#include +#include +#include +#include "hantrommu.h" +#include "subsys.h" + +MODULE_DESCRIPTION("Verisilicon VPU Driver"); +MODULE_LICENSE("GPL"); + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#define MMU_REG_OFFSET 0 +#define MMU_REG_HW_ID (MMU_REG_OFFSET + 6*4) +#define MMU_REG_FLUSH (MMU_REG_OFFSET + 97*4) +#define MMU_REG_PAGE_TABLE_ID (MMU_REG_OFFSET + 107*4) +#define MMU_REG_CONTROL (MMU_REG_OFFSET + 226*4) +#define MMU_REG_ADDRESS (MMU_REG_OFFSET + 227*4) +#define MMU_REG_ADDRESS_MSB (MMU_REG_OFFSET + 228*4) + +/******************************************************************************* +***** New MMU Defination *******************************************************/ +#define MMU_MTLB_SHIFT 22 +#define MMU_STLB_4K_SHIFT 12 +#define MMU_STLB_64K_SHIFT 16 + +#define MMU_MTLB_BITS (32 - MMU_MTLB_SHIFT) +#define MMU_PAGE_4K_BITS MMU_STLB_4K_SHIFT +#define MMU_STLB_4K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS) +#define MMU_PAGE_64K_BITS MMU_STLB_64K_SHIFT +#define MMU_STLB_64K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_64K_BITS) + +#define MMU_MTLB_ENTRY_NUM (1 << MMU_MTLB_BITS) +#define MMU_MTLB_SIZE (MMU_MTLB_ENTRY_NUM << 2) +#define MMU_STLB_4K_ENTRY_NUM (1 << MMU_STLB_4K_BITS) +#define MMU_STLB_4K_SIZE (MMU_STLB_4K_ENTRY_NUM << 2) +#define MMU_PAGE_4K_SIZE (1 << MMU_STLB_4K_SHIFT) +#define MMU_STLB_64K_ENTRY_NUM (1 << MMU_STLB_64K_BITS) +#define MMU_STLB_64K_SIZE (MMU_STLB_64K_ENTRY_NUM << 2) +#define MMU_PAGE_64K_SIZE (1 << MMU_STLB_64K_SHIFT) + +#define MMU_MTLB_MASK (~((1U << MMU_MTLB_SHIFT)-1)) +#define MMU_STLB_4K_MASK ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK) +#define MMU_PAGE_4K_MASK (MMU_PAGE_4K_SIZE - 1) +#define MMU_STLB_64K_MASK ((~((1U << MMU_STLB_64K_SHIFT)-1)) ^ MMU_MTLB_MASK) +#define MMU_PAGE_64K_MASK (MMU_PAGE_64K_SIZE - 1) + +/* Page offset definitions. */ +#define MMU_OFFSET_4K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS) +#define MMU_OFFSET_4K_MASK ((1U << MMU_OFFSET_4K_BITS) - 1) +#define MMU_OFFSET_16K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_16K_BITS) +#define MMU_OFFSET_16K_MASK ((1U << MMU_OFFSET_16K_BITS) - 1) + +#define MMU_MTLB_ENTRY_HINTS_BITS 6 +#define MMU_MTLB_ENTRY_STLB_MASK (~((1U << MMU_MTLB_ENTRY_HINTS_BITS) - 1)) + +#define MMU_MTLB_PRESENT 0x00000001 +#define MMU_MTLB_EXCEPTION 0x00000002 +#define MMU_MTLB_4K_PAGE 0x00000000 + +#define MMU_STLB_PRESENT 0x00000001 +#define MMU_STLB_EXCEPTION 0x00000002 +#define MMU_STLB_4K_PAGE 0x00000000 + +#define MMU_FALSE 0 +#define MMU_TRUE 1 + +#define MMU_ERR_OS_FAIL (0xffff) +#define MMU_EFAULT MMU_ERR_OS_FAIL +#define MMU_ENOTTY MMU_ERR_OS_FAIL + +#define MMU_INFINITE ((u32) ~0U) + +#define MAX_NOPAGED_SIZE 0x20000 +#define MMU_SUPPRESS_OOM_MESSAGE 1 + +#define MTLB_PCIE_START_ADDRESS 0x00100000 +#define PAGE_PCIE_START_ADDRESS 0x00200000 /* page_table_entry start address */ +#define STLB_PCIE_START_ADDRESS 0x00300000 +#define PAGE_TABLE_ENTRY_SIZE 64 + +#if MMU_SUPPRESS_OOM_MESSAGE +#define MMU_NOWARN __GFP_NOWARN +#else +#define MMU_NOWARN 0 +#endif + +#define MMU_IS_ERROR(status) (status < 0) +#define MMU_NO_ERROR(status) (status >= 0) +#define MMU_IS_SUCCESS(status) (status == MMU_STATUS_OK) + +#undef MMUDEBUG +#ifdef HANTROMMU_DEBUG +# ifdef __KERNEL__ +# define MMUDEBUG(fmt, args...) printk( KERN_INFO "hantrommu: " fmt, ## args) +# else +# define MMUDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define MMUDEBUG(fmt, args...) +#endif + +#define MMU_ON_ERROR(func) \ + do { \ + status = func; \ + if (MMU_IS_ERROR(status)){ \ + goto onerror; \ + } \ + }while (MMU_FALSE) + +#define WritePageEntry(page_entry, entry_value) \ + *(unsigned int *)(page_entry) =(unsigned int)(entry_value) + +#define ReadPageEntry(page_entry) *(unsigned int *)(page_entry) + +#define DRIVER_NAME "hantrodecdma" + +/* simple map mode: generate mmu address which is same as input bus address*/ +unsigned int simple_map = 0; +/* this shift should be an integral multiple of mmu page size(4096). + It can generate a mmu address shift in simple map mode*/ +unsigned int map_shift = 0; + +/* module_param(name, type, perm) */ +module_param(simple_map, uint, 0); +module_param(map_shift, uint, 0); + +enum MMURegion { + MMU_REGION_IN, + MMU_REGION_OUT, + MMU_REGION_PRIVATE, + MMU_REGION_PUB, + + MMU_REGION_COUNT +}; + +struct MMUNode { + void *buf_virtual_address; + unsigned int buf_bus_address; /* used in kernel map mode */ + int mtlb_start; + int stlb_start; + int mtlb_end; + int stlb_end; + unsigned int page_count; + int process_id; + struct file* filp; + + struct MMUNode *next; + struct MMUNode *prev; +}; + +struct MMUDDRRegion { + unsigned long long physical_address; + unsigned long long virtual_address; + unsigned int page_count; + + void *node_mutex; + struct MMUNode *simple_map_head; + struct MMUNode *simple_map_tail; + struct MMUNode *free_map_head; + struct MMUNode *map_head; + struct MMUNode *free_map_tail; + struct MMUNode *map_tail; +}; + +struct MMU { + void *page_table_mutex; + /* Master TLB information. */ + unsigned int mtlb_size; + unsigned long long mtlb_physical; + void *mtlb_virtual; + unsigned int mtlb_entries; + + int enabled; + unsigned int stlb_size; + unsigned long long stlb_physical; + void *stlb_virtual; + struct MMUDDRRegion region[MMU_REGION_COUNT]; + unsigned int page_table_array_size; + unsigned long long page_table_array_physical; + void *page_table_array; +}; + +static struct MMU *g_mmu = NULL; +extern unsigned long gBaseDDRHw; +unsigned int mmu_enable = MMU_FALSE; +static unsigned int mmu_init = MMU_FALSE; +extern unsigned int pcie; +static unsigned int region_in_mmu_start = REGION_IN_MMU_START; +static unsigned int region_in_mmu_end = REGION_IN_MMU_END; +static unsigned int region_out_mmu_start = REGION_OUT_MMU_START; +static unsigned int region_out_mmu_end = REGION_OUT_MMU_END; +static unsigned int region_private_mmu_start = REGION_PRIVATE_MMU_START; +static unsigned int region_private_mmu_end = REGION_PRIVATE_MMU_END; + +static const struct platform_device_info hantro_platform_info = { + .name = DRIVER_NAME, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int hantro_drm_probe(struct platform_device *pdev) +{ + int result; + struct device *dev = &pdev->dev; + (void) dev; + (void) result; + return 0; +} +static int hantro_drm_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + (void) dev; + return 0; +} +static const struct platform_device_id hantro_drm_platform_ids[] = { + { + .name = DRIVER_NAME, + }, + {/* sentinel */ }, +}; +static const struct of_device_id hantro_of_match[] = { + { .compatible = "thead,light-vc8000d-mmu", }, + {/* sentinel */} +}; +static struct platform_driver hantro_drm_platform_driver = { + .probe = hantro_drm_probe, + .remove = hantro_drm_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = hantro_of_match, + }, + .id_table = hantro_drm_platform_ids, +}; + +struct platform_device *platformdev; + +static enum MMUStatus ZeroMemory(void *memory, unsigned int bytes) { + memset(memory, 0, bytes); + + return MMU_STATUS_OK; +} + +static enum MMUStatus AllocateMemory(unsigned int bytes, void **memory){ + void *pointer; + enum MMUStatus status; + + if (bytes > MAX_NOPAGED_SIZE) { + pointer = (void*) vmalloc(bytes); + MMUDEBUG(" *****VMALLOC size*****%d\n", bytes); + } else { + pointer = (void*) kmalloc(bytes, GFP_KERNEL | MMU_NOWARN); + MMUDEBUG(" *****KMALLOC size*****%d\n", bytes); + } + + if (pointer == NULL) { + /* Out of memory. */ + status = MMU_STATUS_OUT_OF_MEMORY; + goto onerror; + } + + /* Return pointer to the memory allocation. */ + *memory = pointer; + + return MMU_STATUS_OK; + +onerror: + /* Return the status. */ + return status; +} + +static enum MMUStatus FreeMemory(void *memory) { + /* Free the memory from the OS pool. */ + if (is_vmalloc_addr(memory)) { + MMUDEBUG(" *****VFREE*****%p\n", memory); + vfree(memory); + } else { + MMUDEBUG(" *****KFREE*****%p\n", memory); + kfree(memory); + } + return MMU_STATUS_OK; +} + + +static enum MMUStatus SMDeleteNode(struct MMUNode **pp) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + + MMUDEBUG(" *****DeleteNode size*****%d\n", (*pp)->page_count); + FreeMemory(*pp); + + return MMU_STATUS_OK; +} + +static enum MMUStatus DeleteNode(struct MMUNode **pp) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + + MMUDEBUG(" *****DeleteNode size*****%d\n", (*pp)->page_count); + FreeMemory(*pp); + + return MMU_STATUS_OK; +} + +static enum MMUStatus MergeNode(struct MMUNode *h, + struct MMUNode **pp) { + struct MMUNode *tmp0 = h->next; + struct MMUNode *tmp1 = h->next; + while(tmp0) { + /* 1th step: find front contiguous memory node */ + if(tmp0->mtlb_end == (*pp)->mtlb_start && + tmp0->stlb_end == (*pp)->stlb_start) { + tmp0->mtlb_end = (*pp)->mtlb_end; + tmp0->stlb_end = (*pp)->stlb_end; + tmp0->page_count += (*pp)->page_count; + DeleteNode(pp); + MMUDEBUG(" *****first merge to front. node size*****%d\n", tmp0->page_count); + /* after merge to front contiguous memory node, + find if there is behind contiguous memory node */ + while(tmp1) { + /* merge */ + if(tmp1->mtlb_start == tmp0->mtlb_end && + tmp1->stlb_start == tmp0->stlb_end) { + tmp1->mtlb_start = tmp0->mtlb_start; + tmp1->stlb_start = tmp0->stlb_start; + tmp1->page_count += tmp0->page_count; + MMUDEBUG(" *****second merge to behind. node size*****%d\n", tmp1->page_count); + DeleteNode(&tmp0); + return MMU_STATUS_OK; + } + tmp1 = tmp1->next; + } + return MMU_STATUS_OK; + /* 1th step: find behind contiguous memory node */ + } else if(tmp0->mtlb_start == (*pp)->mtlb_end && + tmp0->stlb_start == (*pp)->stlb_end) { + tmp0->mtlb_start = (*pp)->mtlb_start; + tmp0->stlb_start = (*pp)->stlb_start; + tmp0->page_count += (*pp)->page_count; + DeleteNode(pp); + MMUDEBUG(" *****first merge to behind. node size*****%d\n", tmp0->page_count); + /* after merge to behind contiguous memory node, + find if there is front contiguous memory node */ + while(tmp1) { + /* merge */ + if(tmp1->mtlb_end == tmp0->mtlb_start && + tmp1->stlb_end == tmp0->stlb_start) { + tmp1->mtlb_end = tmp0->mtlb_end; + tmp1->stlb_end = tmp0->stlb_end; + tmp1->page_count += tmp0->page_count; + MMUDEBUG(" *****second merge to front. node size*****%d\n", tmp1->page_count); + DeleteNode(&tmp0); + return MMU_STATUS_OK; + } + tmp1 = tmp1->next; + } + return MMU_STATUS_OK; + } + tmp0 = tmp0->next; + } + return MMU_STATUS_FALSE; +} + +/* Insert a node to map list */ +static enum MMUStatus SMInsertNode(enum MMURegion e, + struct MMUNode **pp) { + struct MMUNode *h; + + h = g_mmu->region[e].simple_map_head; + + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + MMUDEBUG(" *****insert bm node*****%d\n", (*pp)->page_count); + + return MMU_STATUS_OK; +} + +static enum MMUStatus InsertNode(enum MMURegion e, + struct MMUNode **pp, + unsigned int free) { + enum MMUStatus status; + struct MMUNode *h, *b; + + if(free) { + h = g_mmu->region[e].free_map_head; + b = g_mmu->region[e].map_head; + status = MergeNode(h, pp); + MMUDEBUG(" *****insert free*****%d\n", (*pp)->page_count); + if(MMU_IS_ERROR(status)) { + /* remove from map*/ + if((*pp)->prev != NULL && (*pp)->next != NULL) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + } + /* insert to free map */ + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + } + } else { + h = g_mmu->region[e].map_head; + + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + MMUDEBUG(" *****insert unfree*****%d\n", (*pp)->page_count); + } + + return MMU_STATUS_OK; +} + +/* Create a Node */ +static enum MMUStatus SMCreateNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p, **new; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + new = &p; + + (*new)->mtlb_start = -1; + (*new)->stlb_start = -1; + (*new)->mtlb_end = -1; + (*new)->stlb_end = -1; + (*new)->process_id = 0; + (*new)->filp = NULL; + (*new)->page_count = 0; + (*new)->prev = NULL; + (*new)->next = NULL; + /* Insert a uncomplete Node, it will be initialized later */ + SMInsertNode(e, new); + + /* return a new node for map buffer */ + *node = *new; + return MMU_STATUS_OK; +} + +/* Create initial Nodes */ +static enum MMUStatus SMCreateNodes(void) { + struct MMUNode *simple_map_head; + struct MMUNode *simple_map_tail; + int i; + + /* Init each region map node */ + for (i = 0; i < MMU_REGION_COUNT ; i++) { + simple_map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + simple_map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + + simple_map_head->mtlb_start = -1; + simple_map_head->stlb_start = -1; + simple_map_head->mtlb_end = -1; + simple_map_head->stlb_end = -1; + simple_map_head->process_id = 0; + simple_map_head->filp = NULL; + simple_map_head->page_count = 0; + simple_map_head->prev = NULL; + simple_map_head->next = simple_map_tail; + + simple_map_tail->mtlb_start = -1; + simple_map_tail->stlb_start = -1; + simple_map_tail->mtlb_end = -1; + simple_map_tail->stlb_end = -1; + simple_map_tail->process_id = 0; + simple_map_tail->filp = NULL; + simple_map_tail->page_count = 0; + simple_map_tail->prev = simple_map_head; + simple_map_tail->next = NULL; + + g_mmu->region[i].simple_map_head = simple_map_head; + g_mmu->region[i].simple_map_tail = simple_map_tail; + } + return MMU_STATUS_OK; +} + +static enum MMUStatus CreateNode(void) { + struct MMUNode *free_map_head, *map_head, *p, **pp; + struct MMUNode *free_map_tail, *map_tail; + int i; + unsigned int page_count; + unsigned int prev_stlb = 0, prev_mtlb = 0; + + /* Init each region map node */ + for (i = 0; i < MMU_REGION_COUNT ; i++) { + free_map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + free_map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + + free_map_head->mtlb_start = map_head->mtlb_start = -1; + free_map_head->stlb_start = map_head->stlb_start = -1; + free_map_head->mtlb_end = map_head->mtlb_end = -1; + free_map_head->stlb_end = map_head->stlb_end = -1; + free_map_head->process_id = map_head->process_id = 0; + free_map_head->filp = map_head->filp = NULL; + free_map_head->page_count = map_head->page_count = 0; + free_map_head->prev = map_head->prev = NULL; + free_map_head->next = free_map_tail; + map_head->next = map_tail; + + free_map_tail->mtlb_start = map_tail->mtlb_start = -1; + free_map_tail->stlb_start = map_tail->stlb_start = -1; + free_map_tail->mtlb_end = map_tail->mtlb_end = -1; + free_map_tail->stlb_end = map_tail->stlb_end = -1; + free_map_tail->process_id = map_tail->process_id = 0; + free_map_tail->filp = map_tail->filp = NULL; + free_map_tail->page_count = map_tail->page_count = 0; + free_map_tail->prev = free_map_head; + map_tail->prev = map_head; + free_map_tail->next = map_tail->next = NULL; + + g_mmu->region[i].free_map_head = free_map_head; + g_mmu->region[i].map_head = map_head; + g_mmu->region[i].free_map_tail = free_map_tail; + g_mmu->region[i].map_tail = map_tail; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + pp = &p; + + switch(i) { + case MMU_REGION_IN: + page_count = (REGION_IN_END - REGION_IN_START + 1)/PAGE_SIZE; + p->stlb_start = region_in_mmu_start >> 12 & 0x3FF; //hold mmu addr: 0x0 + p->mtlb_start = region_in_mmu_start >> 22; + //end point next region start: +1; for remainder: +1 + p->stlb_end = prev_stlb = region_in_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_in_mmu_end >> 22; + p->page_count = page_count - 1; //hold mmu addr: 0x0 + break; + case MMU_REGION_OUT: + page_count = (REGION_OUT_END - REGION_OUT_START + 1)/PAGE_SIZE; + p->stlb_start = region_out_mmu_start >> 12 & 0x3FF; + p->mtlb_start = region_out_mmu_start >> 22; + p->stlb_end = prev_stlb = region_out_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_out_mmu_end >> 22; + p->page_count = page_count; + break; + case MMU_REGION_PRIVATE: + page_count = (REGION_PRIVATE_END - REGION_PRIVATE_START + 1)/PAGE_SIZE; + p->stlb_start = region_private_mmu_start >> 12 & 0x3FF; + p->mtlb_start = region_private_mmu_start >> 22; + p->stlb_end = prev_stlb = region_private_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_private_mmu_end >> 22; + p->page_count = page_count; + break; + case MMU_REGION_PUB: + p->stlb_start = prev_stlb; + p->mtlb_start = prev_mtlb; + p->stlb_end = prev_stlb = MMU_STLB_4K_ENTRY_NUM - 1; + p->mtlb_end = prev_mtlb = MMU_MTLB_ENTRY_NUM - 1; + p->page_count = (p->mtlb_end - p->mtlb_start) * MMU_STLB_4K_ENTRY_NUM + + p->stlb_end - p->stlb_start + 1; + break; + default: + pr_notice(" *****MMU Region Error*****\n"); + break; + } + + p->process_id = 0; + p->filp = NULL; + p->next = p->prev = NULL; + + InsertNode(i, pp, 1); + } + + return MMU_STATUS_OK; +} + +/* A simpile function to check if the map buffer is existed. + it needs more complex version*/ +static enum MMUStatus SMCheckAddress(enum MMURegion e, + void *virtual_address) { + struct MMUNode *p; + p = g_mmu->region[e].simple_map_head->next; + + while(p) { + if(p->buf_virtual_address == virtual_address) { + return MMU_STATUS_FALSE; + } + p = p->next; + } + return MMU_STATUS_OK; +} + +static enum MMUStatus FindFreeNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p; + p = g_mmu->region[e].free_map_head->next; + + while(p) { + if(p->page_count >= page_count) { + *node = p; + return MMU_STATUS_OK; + } + p = p->next; + } + return MMU_STATUS_FALSE; +} + +static enum MMUStatus SplitFreeNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p, **new; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + new = &p; + + **new = **node; + + (*new)->mtlb_start = (*node)->mtlb_start; + (*new)->stlb_start = (*node)->stlb_start; + (*new)->mtlb_end = (page_count + (*node)->stlb_start) / + MMU_STLB_4K_ENTRY_NUM + + (*node)->mtlb_start; + (*new)->stlb_end = (page_count + (*node)->stlb_start) % + MMU_STLB_4K_ENTRY_NUM; + (*new)->process_id = (*node)->process_id; + (*new)->page_count = page_count; + + MMUDEBUG(" *****new mtlb_start*****%d\n", (*new)->mtlb_start); + MMUDEBUG(" *****new stlb_start*****%d\n", (*new)->stlb_start); + MMUDEBUG(" *****new mtlb_end*****%d\n", (*new)->mtlb_end); + MMUDEBUG(" *****new stlb_end*****%d\n", (*new)->stlb_end); + /* Insert a new node in map */ + InsertNode(e, new, 0); + + /* Update free node in free map*/ + (*node)->page_count -= page_count; + if((*node)->page_count == 0) { + DeleteNode(node); + MMUDEBUG(" *****old node deleted*****\n"); + } else { + (*node)->mtlb_start = (*new)->mtlb_end; + (*node)->stlb_start = (*new)->stlb_end; + + MMUDEBUG(" *****old mtlb_start*****%d\n", (*node)->mtlb_start); + MMUDEBUG(" *****old stlb_start*****%d\n", (*node)->stlb_start); + MMUDEBUG(" *****old mtlb_end*****%d\n", (*node)->mtlb_end); + MMUDEBUG(" *****old stlb_end*****%d\n", (*node)->stlb_end); + } + /* return a new node for map buffer */ + *node = *new; + + return MMU_STATUS_OK; +} + +static enum MMUStatus SMRemoveNode(enum MMURegion e, + void *buf_virtual_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].simple_map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_virtual_address == buf_virtual_address && + (*pp)->process_id == process_id) { + SMDeleteNode(pp); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus RemoveNode(enum MMURegion e, + void *buf_virtual_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_virtual_address == buf_virtual_address && + (*pp)->process_id == process_id) { + InsertNode(e, pp, 1); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus SMRemoveKernelNode(enum MMURegion e, + unsigned int buf_bus_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].simple_map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_bus_address == buf_bus_address && + (*pp)->process_id == process_id) { + SMDeleteNode(pp); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus RemoveKernelNode(enum MMURegion e, + unsigned int buf_bus_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_bus_address == buf_bus_address && + (*pp)->process_id == process_id) { + InsertNode(e, pp, 1); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus Delay(unsigned int delay) { + if(delay > 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) + ktime_t dl = ktime_set((delay / MSEC_PER_SEC), + (delay % MSEC_PER_SEC) * NSEC_PER_MSEC); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&dl, HRTIMER_MODE_REL); +#else + msleep(delay); +#endif + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus CreateMutex(void **mtx) { + enum MMUStatus status; + + /* Allocate the mutex structure. */ + status = AllocateMemory(sizeof(struct mutex), mtx); + if (MMU_IS_SUCCESS(status)) { + /* Initialize the mutex. */ + mutex_init(*(struct mutex **)mtx); + } + + return status; +} + +static enum MMUStatus DeleteMutex(void *mtx) { + /* Destroy the mutex. */ + mutex_destroy((struct mutex *)mtx); + + /* Free the mutex structure. */ + FreeMemory(mtx); + + return MMU_STATUS_OK; +} + +static enum MMUStatus AcquireMutex(void *mtx, unsigned int timeout) { + if (timeout == MMU_INFINITE) + { + /* Lock the mutex. */ + mutex_lock(mtx); + + /* Success. */ + return MMU_STATUS_OK; + } + + for (;;) { + /* Try to acquire the mutex. */ + if (mutex_trylock(mtx)) { + /* Success. */ + return MMU_STATUS_OK; + } + + if (timeout-- == 0) { + break; + } + + /* Wait for 1 millisecond. */ + Delay(1); + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus ReleaseMutex(void *mtx) { + /* Release the mutex. */ + mutex_unlock(mtx); + + return MMU_STATUS_OK; +} + + +static inline enum MMUStatus QueryProcessPageTable(void *logical, + unsigned long long *address) { + unsigned long lg = (unsigned long)logical; + unsigned long offset = lg & ~PAGE_MASK; + struct vm_area_struct *vma; + spinlock_t *ptl; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (is_vmalloc_addr(logical)) { + /* vmalloc area. */ + *address = page_to_phys(vmalloc_to_page(logical)) | offset; + return MMU_STATUS_OK; + } else if (virt_addr_valid(lg)) { + /* Kernel logical address. */ + *address = virt_to_phys(logical); + return MMU_STATUS_OK; + } else { + /* Try user VM area. */ + if (!current->mm) + return MMU_STATUS_NOT_FOUND; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + down_read(¤t->mm->mmap_lock); +#else + down_read(¤t->mm->mmap_sem); +#endif + vma = find_vma(current->mm, lg); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + up_read(¤t->mm->mmap_lock); +#else + up_read(¤t->mm->mmap_sem); +#endif + + /* To check if mapped to user. */ + if (!vma) + return MMU_STATUS_NOT_FOUND; + + pgd = pgd_offset(current->mm, lg); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + return MMU_STATUS_NOT_FOUND; + +#if (defined(CONFIG_CPU_CSKYV2) || defined(CONFIG_X86)) \ + && LINUX_VERSION_CODE >= KERNEL_VERSION (4,12,0) + pud = pud_offset((p4d_t*)pgd, lg); +#elif (defined(CONFIG_CPU_CSKYV2) || defined(CONFIG_RISCV)) \ + && LINUX_VERSION_CODE >= KERNEL_VERSION (4,11,0) + pud = pud_offset((p4d_t*)pgd, lg); +#else + pud = pud_offset(pgd, lg); +#endif + if (pud_none(*pud) || pud_bad(*pud)) + return MMU_STATUS_NOT_FOUND; + + pmd = pmd_offset(pud, lg); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + return MMU_STATUS_NOT_FOUND; + + pte = pte_offset_map_lock(current->mm, pmd, lg, &ptl); + if (!pte) { + spin_unlock(ptl); + return MMU_STATUS_NOT_FOUND; + } + + if (!pte_present(*pte)) { + pte_unmap_unlock(pte, ptl); + return MMU_STATUS_NOT_FOUND; + } + + *address = (pte_pfn(*pte) << PAGE_SHIFT) | offset; + pte_unmap_unlock(pte, ptl); + + *address -= gBaseDDRHw; + //MMUDEBUG(" QueryProcessPageTable map: virt %p -> %p\n", logical, (void *)*address); + + return MMU_STATUS_OK; + } +} + +static inline int GetProcessID(void) { + return current->tgid; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +static inline int is_vmalloc_addr(void *addr) { + unsigned long long addr = (unsigned long long)Addr; + + return addr >= VMALLOC_START && addr < VMALLOC_END; +} +#endif + +static enum MMUStatus GetPhysicalAddress(void *logical, + unsigned long long *address) { + enum MMUStatus status; + + status = QueryProcessPageTable(logical, address); + + return status; +} + +static enum MMUStatus GetPageEntry(struct MMUNode *node, + unsigned int **page_table_entry, + unsigned int i) { + int num = node->mtlb_start * MMU_STLB_4K_ENTRY_NUM + + node->stlb_start + i; + *page_table_entry = (unsigned int*)g_mmu->stlb_virtual + num; + return MMU_STATUS_OK; +} + +static enum MMUStatus SetupDynamicSpace(void) { + int i; + enum MMUStatus status; + unsigned int stlb_entry; + void *pointer; + unsigned long long address; + dma_addr_t dma_handle; + unsigned int num_entries = MMU_MTLB_ENTRY_NUM; + unsigned int *mtlb_virtual = (unsigned int *)g_mmu->mtlb_virtual; + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + if(pcie) { + pointer = ioremap(gBaseDDRHw+STLB_PCIE_START_ADDRESS, num_entries*MMU_STLB_4K_SIZE); + g_mmu->stlb_virtual = pointer; + MMUDEBUG(" *****stlb_virtual = %p**%d\n", pointer, num_entries*MMU_STLB_4K_SIZE); + address = STLB_PCIE_START_ADDRESS; + for(i = 0; i < num_entries; i++){ + stlb_entry = address + /* 4KB page size */ + | (0 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(mtlb_virtual++, stlb_entry); + address += MMU_STLB_4K_SIZE; + } + + } else { + g_mmu->stlb_virtual = (void *)((u64)(g_mmu->mtlb_virtual) + MMU_MTLB_SIZE); + g_mmu->stlb_physical = address = g_mmu->mtlb_physical + MMU_MTLB_SIZE; + g_mmu->stlb_size = num_entries * MMU_STLB_4K_SIZE; + + for(i = 0; i < num_entries; i++){ + stlb_entry = address + /* 4KB page size */ + | (0 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(mtlb_virtual++, stlb_entry); + address += MMU_STLB_4K_SIZE; + } + } + ReleaseMutex(g_mmu->page_table_mutex); + + /* Initial map info. */ + if (simple_map) + SMCreateNodes(); + else + CreateNode(); + + return MMU_STATUS_OK; +onerror: + /* Return status. */ + return status; +} + + +enum MMUStatus MMUInit(volatile unsigned char *hwregs) { + enum MMUStatus status; + unsigned i; + int result; + void *pointer; + + if (mmu_init == MMU_TRUE) { + /* All mmu use common table and dev, just initial once*/ + pr_notice(" *****MMU Already Initialed*****\n"); + return MMU_STATUS_OK; + } + + if(!hwregs || (ioread32((void*)(hwregs + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_NOT_FOUND; + + pr_notice(" *****MMU Init*****\n"); + + platformdev = platform_device_register_full(&hantro_platform_info); + if(platformdev == NULL) { + pr_err("hantrodec create platform device fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } else { + pr_info("Create platform device success\n"); + } + + result = platform_driver_register(&hantro_drm_platform_driver); + pr_notice("Platform driver status is %d\n", result); + + /* Allocate memory for the MMU object. */ + MMU_ON_ERROR(AllocateMemory(sizeof(struct MMU), &pointer)); + ZeroMemory(pointer, sizeof(struct MMU)); + + g_mmu = pointer; + + g_mmu->page_table_mutex = NULL; + + /* Create the page table mutex. */ + MMU_ON_ERROR(CreateMutex(&g_mmu->page_table_mutex)); + + for (i = 0; i < MMU_REGION_COUNT;i++) { + MMU_ON_ERROR(CreateMutex(&g_mmu->region[i].node_mutex)); + } + + mmu_init = MMU_TRUE; + return MMU_STATUS_OK; + +onerror: + pr_notice(" *****MMU Init Error*****\n"); + return status; +} + +enum MMUStatus MMURelease(void *filp, volatile unsigned char *hwregs) { + int i, j; + struct MMUNode *p, *tmp; + unsigned long long address; + unsigned int *page_table_entry; + + if(!hwregs || (ioread32((void*)(hwregs + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + + /* if mmu or TLB not enabled, return */ + if (simple_map) { + if(g_mmu == NULL || g_mmu->region[0].simple_map_head == NULL) + return MMU_STATUS_OK; + } else { + if(g_mmu == NULL || g_mmu->region[0].map_head == NULL) + return MMU_STATUS_OK; + } + + pr_notice(" *****MMU Release*****\n"); + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + + if (simple_map) { + for (i = 0; i < MMU_REGION_COUNT; i++) { + p = g_mmu->region[i].simple_map_head->next; + + while(p) { + tmp = p->next; + if(p->filp == (struct file *)filp) { + + for(j = 0;j < p->page_count; j++) { + GetPageEntry(p, &page_table_entry, j); + address = 0; + WritePageEntry(page_table_entry, address); + } + + SMRemoveNode(i, p->buf_virtual_address, p->process_id); + } + p = tmp; + } + } + } else { + for (i = 0; i < MMU_REGION_COUNT; i++) { + p = g_mmu->region[i].map_head->next; + + while(p) { + tmp = p->next; + if(p->filp == (struct file *)filp) { + + for(j = 0;j < p->page_count; j++) { + GetPageEntry(p, &page_table_entry, j); + address = 0; + WritePageEntry(page_table_entry, address); + } + + RemoveNode(i, p->buf_virtual_address, p->process_id); + } + p = tmp; + } + } + } + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; +} + +enum MMUStatus MMUCleanup(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + int i; + struct MMUNode *p, *tmp; + struct MMUNode *fp; + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL && + (ioread32((void*)(hwregs[i][0] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + if (hwregs[i][1] != NULL && + (ioread32((void*)(hwregs[i][1] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + } + + pr_info(" *****MMU cleanup*****\n"); + if (pcie) { + if (g_mmu->stlb_virtual) + iounmap(g_mmu->stlb_virtual); + if (g_mmu->mtlb_virtual) + iounmap(g_mmu->mtlb_virtual); + if (g_mmu->page_table_array) + iounmap(g_mmu->page_table_array); + } else { + /* stlb_virtual is same alloc on alloc mtlb_virtual in func MMUEnable() + * so, should not free g_mmu->stlb_virtual.But free handle g_mmu->mtlb_physical + * size should be ( g_mmu->mtlb_size+g_mmu->stlb_size) + */ + if (g_mmu->mtlb_virtual) + dma_free_coherent(&platformdev->dev, g_mmu->mtlb_size+g_mmu->stlb_size, + g_mmu->mtlb_virtual, (dma_addr_t)g_mmu->mtlb_physical); + if (g_mmu->page_table_array) + dma_free_coherent(&platformdev->dev, g_mmu->page_table_array_size, + g_mmu->page_table_array, (dma_addr_t)g_mmu->page_table_array_physical); + } + DeleteMutex(g_mmu->page_table_mutex); + + for (i = 0; i < MMU_REGION_COUNT; i++) { + DeleteMutex(g_mmu->region[i].node_mutex); + if (simple_map) { + p = g_mmu->region[i].simple_map_head; + while(p) { + tmp = p->next; + FreeMemory(p); + p = tmp; + MMUDEBUG(" *****clean node*****\n"); + } + } else { + fp = g_mmu->region[i].free_map_head; + p = g_mmu->region[i].map_head; + while(fp) { + tmp = fp->next; + FreeMemory(fp); + fp = tmp; + MMUDEBUG(" *****clean free node*****\n"); + } + + while(p) { + tmp = p->next; + FreeMemory(p); + p = tmp; + MMUDEBUG(" *****clean node*****\n"); + } + } + } + FreeMemory(g_mmu); + + platform_device_unregister(platformdev); + platform_driver_unregister(&hantro_drm_platform_driver); + pr_info("Unregister platform device.\n"); + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) + iowrite32(0, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + if (hwregs[i][1] != NULL) + iowrite32(0, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + mmu_enable = 0; + mmu_init = 0; + + return MMU_STATUS_OK; +} + +/*------------------------------------------------------------------------------ + Function name: MMUEnable + Description: + Create TLB, set registers and enable MMU + + For pcie, TLB buffers come from FPGA memory and The distribution is as follows + MTLB: start from: 0x00100000, size: 4K bits + page table array: 0x00200000 64 bits + STLB: 0x00300000 4M bits + ------------------------------------------------------------------------------*/ +enum MMUStatus MMUEnable(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + enum MMUStatus status; + unsigned int address; + unsigned int mutex = MMU_FALSE; + dma_addr_t dma_handle; + u32 i = 0; + u32 address_ext; + u32 total_table_size = 0; + + if(mmu_enable == MMU_TRUE) { + pr_info(" *****MMU Already Enabled*****\n"); + return MMU_STATUS_OK; + } + + pr_info(" *****MMU Enable...*****\n"); + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + if(pcie) { + g_mmu->mtlb_size = MMU_MTLB_SIZE; + g_mmu->mtlb_virtual = ioremap(gBaseDDRHw+MTLB_PCIE_START_ADDRESS, g_mmu->mtlb_size); + g_mmu->mtlb_physical = MTLB_PCIE_START_ADDRESS; + + g_mmu->page_table_array = ioremap(gBaseDDRHw+PAGE_PCIE_START_ADDRESS, PAGE_TABLE_ENTRY_SIZE); + } else { + /* Allocate the 4K mode MTLB table. */ + total_table_size = MMU_MTLB_SIZE + MMU_MTLB_ENTRY_NUM*MMU_STLB_4K_SIZE; + g_mmu->mtlb_size = MMU_MTLB_SIZE; + g_mmu->mtlb_virtual = dma_alloc_coherent(&platformdev->dev, total_table_size, + &dma_handle, GFP_KERNEL | GFP_DMA); + MMUDEBUG(" *****g_mmu->mtlb_virtual = 0x%p\n", g_mmu->mtlb_virtual); + g_mmu->mtlb_physical = (unsigned long long)dma_handle; + MMUDEBUG(" *****mtlb_physical = 0x%llx\n", (unsigned int)g_mmu->mtlb_physical); + if(g_mmu->mtlb_virtual == NULL) { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + + g_mmu->page_table_array_size = PAGE_TABLE_ENTRY_SIZE; + g_mmu->page_table_array = dma_alloc_coherent(&platformdev->dev, g_mmu->page_table_array_size, + &dma_handle, GFP_KERNEL | GFP_DMA); + MMUDEBUG(" *****g_mmu->page_table_array = 0x%p\n", g_mmu->page_table_array); + g_mmu->page_table_array_physical = (unsigned long long)dma_handle; + MMUDEBUG(" *****page_table_array_physical = 0x%llx\n", (unsigned int)g_mmu->page_table_array_physical); + if(g_mmu->page_table_array == NULL) { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + } + + *((unsigned int*)g_mmu->page_table_array) = + (g_mmu->mtlb_physical & 0xFFFFF000) | (0 << 0); + *((unsigned int *)g_mmu->page_table_array+1) = + (u32)(g_mmu->mtlb_physical >> 32)&0xff; + *((unsigned int *)g_mmu->page_table_array+2) = + (g_mmu->mtlb_physical & 0xFFFFF000) | (0 << 0); + *((unsigned int *)g_mmu->page_table_array+3) = + (u32)(g_mmu->mtlb_physical >> 32)&0xff; + + MMUDEBUG(" Page table array[0]: lsb = 0x%llx\n", ((int *)g_mmu->page_table_array)[0]); + MMUDEBUG(" msb = 0x%llx\n", ((int *)g_mmu->page_table_array)[1]); + + ZeroMemory(g_mmu->mtlb_virtual, total_table_size); + + ReleaseMutex(g_mmu->page_table_mutex); + + MMU_ON_ERROR(SetupDynamicSpace()); + + if(pcie) { + address = PAGE_PCIE_START_ADDRESS; + } else { + address = g_mmu->page_table_array_physical; + address_ext = ((u32)(g_mmu->page_table_array_physical >> 32))&0xff; + } + + /* set regs of all MMUs */ + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) { + iowrite32(address, (void*)(hwregs[i][0] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][0] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + } + if (hwregs[i][1] != NULL) { + iowrite32(address, (void*)(hwregs[i][1] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][1] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + } + mmu_enable = MMU_TRUE; + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Enable Error*****\n"); + return status; +} + +/*------------------------------------------------------------------------------ + Function name: MMUFlush + Description: + Flush MMU reg to update cache in MMU. + ------------------------------------------------------------------------------*/ +static enum MMUStatus MMUFlush(u32 core_id, volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + enum MMUStatus status; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Flush*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + if (hwregs[core_id][0] != NULL) { + iowrite32(0x10, (void*)(hwregs[core_id][0] + MMU_REG_FLUSH)); + iowrite32(0x00, (void*)(hwregs[core_id][0] + MMU_REG_FLUSH)); + } else { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + if (hwregs[core_id][1] != NULL) { + iowrite32(0x10, (void*)(hwregs[core_id][1] + MMU_REG_FLUSH)); + iowrite32(0x00, (void*)(hwregs[core_id][1] + MMU_REG_FLUSH)); + } + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Flush Error*****\n"); + return status; +} + +static enum MMUStatus MMUMemNodeMap(struct addr_desc *addr, struct file *filp) { + enum MMUStatus status; + unsigned int page_count = 0; + unsigned int i = 0; + struct MMUNode *p; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + enum MMURegion e; + unsigned int mutex = MMU_FALSE; + u32 ext_addr; + u32 page_entry_value = 0; + + MMUDEBUG(" *****MMU Map*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + page_count = (addr->size - 1)/PAGE_SIZE + 1; + + GetPhysicalAddress(addr->virtual_address, &address); + MMUDEBUG(" *****MMU map address*****%x\n", address); + if(address >= REGION_IN_START && + address + addr->size < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address + addr->size < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address + addr->size < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) { + MMU_ON_ERROR(SMCheckAddress(e, addr->virtual_address)); + + SMCreateNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", page_count); + + p->buf_virtual_address = addr->virtual_address; + p->process_id = GetProcessID(); + p->filp = filp; + + p->mtlb_start = ((address + map_shift) >> MMU_MTLB_SHIFT); + p->stlb_start = ((address + map_shift) >> MMU_STLB_4K_SHIFT ) & 0x3FF; + p->mtlb_end = (page_count + p->stlb_start) / MMU_STLB_4K_ENTRY_NUM + + p->mtlb_start; + p->stlb_end = (page_count + p->stlb_start) % MMU_STLB_4K_ENTRY_NUM; + p->page_count = page_count; + + for(i = 0;i < page_count; i++) { + GetPhysicalAddress(addr->virtual_address + i * PAGE_SIZE, &address); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + + /* Purpose of Bare_metal mode: input bus address==mmu address*/ + addr->bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } else { + MMU_ON_ERROR(FindFreeNode(e, &p, page_count)); + + SplitFreeNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", p->page_count); + + p->buf_virtual_address = addr->virtual_address; + p->process_id = GetProcessID(); + p->filp = filp; + + for(i = 0;i < page_count; i++) { + GetPhysicalAddress(addr->virtual_address + i * PAGE_SIZE, &address); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + addr->bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } + MMUDEBUG(" MMU_MTLB_SHIFT %d MMU_STLB_4K_SHIFT %d\n", MMU_MTLB_SHIFT, MMU_STLB_4K_SHIFT); + MMUDEBUG(" MMUMemNodeMap map total %d pages in region %d\nMTLB/STLB starts %d/%d, MTLB/STLB ends %d/%d\n", + page_count, (u32)e, p->mtlb_start, p->stlb_start, p->mtlb_end, p->stlb_end); + MMUDEBUG(" MMUMemNodeMap map %p -> 0x%08x\n", addr->virtual_address, addr->bus_address); + + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Map Error*****\n"); + return status; +} + +static enum MMUStatus MMUMemNodeUnmap(struct addr_desc *addr) { + unsigned int i; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + int process_id = GetProcessID(); + enum MMURegion e = MMU_REGION_COUNT; + enum MMUStatus status = MMU_STATUS_OUT_OF_MEMORY; + struct MMUNode *p; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Unmap*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + GetPhysicalAddress(addr->virtual_address, &address); + if(address >= REGION_IN_START && + address < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) + p = g_mmu->region[e].simple_map_head->next; + else + p = g_mmu->region[e].map_head->next; + /* Reset STLB of the node */ + while(p) { + if(p->buf_virtual_address == addr->virtual_address && + p->process_id == process_id) { + for(i = 0;i < p->page_count; i++) { + GetPageEntry(p, &page_table_entry, i); + address = 0; + WritePageEntry(page_table_entry, address); + } + break; + } + p = p->next; + } + if(!p) + goto onerror; + + if (simple_map) + SMRemoveNode(e, addr->virtual_address, process_id); + else + RemoveNode(e, addr->virtual_address, process_id); + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Unmap Error*****\n"); + return status; +} + +enum MMUStatus MMUKernelMemNodeMap(struct kernel_addr_desc *addr) { + enum MMUStatus status; + unsigned int page_count = 0; + unsigned int i = 0; + struct MMUNode *p; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + enum MMURegion e; + unsigned int mutex = MMU_FALSE; + u32 ext_addr; + u32 page_entry_value = 0; + + MMUDEBUG(" *****MMU Map*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + page_count = (addr->size - 1)/PAGE_SIZE + 1; + + address = addr->bus_address; + MMUDEBUG(" *****MMU map address*****%llx\n", address); + if(address >= REGION_IN_START && + address + addr->size < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address + addr->size < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address + addr->size < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) { + //TODO: should check bus addr + //MMU_ON_ERROR(SMCheckAddress(e, addr->virtual_address)); + + SMCreateNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", page_count); + + p->buf_bus_address = addr->bus_address; + p->process_id = GetProcessID(); + p->filp = NULL; + + p->mtlb_start = ((address + map_shift) >> MMU_MTLB_SHIFT); + p->stlb_start = ((address + map_shift) >> MMU_STLB_4K_SHIFT ) & 0x3FF; + p->mtlb_end = (page_count + p->stlb_start) / MMU_STLB_4K_ENTRY_NUM + + p->mtlb_start; + p->stlb_end = (page_count + p->stlb_start) % MMU_STLB_4K_ENTRY_NUM; + p->page_count = page_count; + + for(i = 0;i < page_count; i++) { + /* this function used in kernel only, so we think it's a contunuous buffer*/ + address += (i ? PAGE_SIZE : 0); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + + /* Purpose of Bare_metal mode: input bus address==mmu address*/ + addr->mmu_bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } else { + MMU_ON_ERROR(FindFreeNode(e, &p, page_count)); + + SplitFreeNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", p->page_count); + + p->buf_bus_address = addr->bus_address; + p->process_id = GetProcessID(); + p->filp = NULL; + + for(i = 0;i < page_count; i++) { + /* this function used in kernel only, so we think it's a contunuous buffer*/ + address += (i ? PAGE_SIZE : 0); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + addr->mmu_bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } + MMUDEBUG(" KERNEL MMU_MTLB_SHIFT %d MMU_STLB_4K_SHIFT %d\n", MMU_MTLB_SHIFT, MMU_STLB_4K_SHIFT); + MMUDEBUG(" MMUKernelMemNodeMap map total %d pages in region %d\nMTLB/STLB starts %d/%d, MTLB/STLB ends %d/%d\n", + page_count, (u32)e, p->mtlb_start, p->stlb_start, p->mtlb_end, p->stlb_end); + MMUDEBUG(" MMUKernelMemNodeMap map 0x%llx -> 0x%08x\n", addr->bus_address, addr->mmu_bus_address); + + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Map Error*****\n"); + return status; +} + +enum MMUStatus MMUKernelMemNodeUnmap(struct kernel_addr_desc *addr) { + unsigned int i; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + int process_id = GetProcessID(); + enum MMURegion e = MMU_REGION_COUNT; + enum MMUStatus status = MMU_STATUS_OUT_OF_MEMORY; + struct MMUNode *p; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Unmap*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + address = addr->bus_address; + if(address >= REGION_IN_START && + address < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) + p = g_mmu->region[e].simple_map_head->next; + else + p = g_mmu->region[e].map_head->next; + /* Reset STLB of the node */ + while(p) { + if(p->buf_bus_address == addr->bus_address && + p->process_id == process_id) { + for(i = 0;i < p->page_count; i++) { + GetPageEntry(p, &page_table_entry, i); + address = 0; + WritePageEntry(page_table_entry, address); + } + break; + } + p = p->next; + } + if(!p) + goto onerror; + + if (simple_map) + SMRemoveKernelNode(e, addr->bus_address, process_id); + else + RemoveKernelNode(e, addr->bus_address, process_id); + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Unmap Error*****\n"); + return status; +} + + +static long MMUCtlBufferMap(struct file *filp, unsigned long arg) { + struct addr_desc addr; + long tmp; + + tmp = copy_from_user(&addr, (void*)arg, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUMemNodeMap(&addr, filp); + + tmp = copy_to_user((void*) arg, &addr, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_to_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + return 0; +} + +static long MMUCtlBufferUnmap(unsigned long arg) { + struct addr_desc addr; + long tmp; + + tmp = copy_from_user(&addr, (void*)arg, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUMemNodeUnmap(&addr); + return 0; +} + +static long MMUCtlFlush(unsigned long arg, volatile unsigned char *hwregs[HXDEC_MAX_CORES][2]) { + unsigned int core_id; + long tmp; + + tmp = copy_from_user(&core_id, (void*)arg, sizeof(unsigned int)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUFlush(core_id, hwregs); + + return 0; +} + +long MMUIoctl(unsigned int cmd, void *filp, unsigned long arg, + volatile unsigned char *hwregs[HXDEC_MAX_CORES][2]) { + + u32 i = 0; + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL && + (ioread32((void*)(hwregs[i][0] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return -MMU_ENOTTY; + if (hwregs[i][1] != NULL && + (ioread32((void*)(hwregs[i][1] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return -MMU_ENOTTY; + } + + switch (cmd) { + case HANTRO_IOCS_MMU_MEM_MAP: { + return (MMUCtlBufferMap((struct file *)filp, arg)); + } + case HANTRO_IOCS_MMU_MEM_UNMAP: { + return (MMUCtlBufferUnmap(arg)); + } + case HANTRO_IOCS_MMU_FLUSH: { + return (MMUCtlFlush(arg, hwregs)); + } + default: + return -MMU_ENOTTY; + } +} + +void MMURestore(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) +{ + if (g_mmu == NULL) + return; + + int i; + unsigned int address; + u32 address_ext; + address = g_mmu->page_table_array_physical; + address_ext = ((u32)(g_mmu->page_table_array_physical >> 32))&0xff; + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) { + iowrite32(address, (void*)(hwregs[i][0] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][0] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + } + if (hwregs[i][1] != NULL) { + iowrite32(address, (void*)(hwregs[i][1] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][1] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + } + +} \ No newline at end of file diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_vcmd.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_vcmd.c new file mode 100644 index 000000000..5bd2c9691 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantro_vcmd.c @@ -0,0 +1,4042 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +/* request_irq(), free_irq() */ +#include +#include + +#include +#include +/* needed for virt_to_phys() */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* our own stuff */ +#include +#include "vcmdswhwregisters.h" +#include "bidirect_list.h" +#include "vcmdswhwregisters.h" +#include "hantrovcmd.h" +#include "subsys.h" + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +//#define HANTRO_VCMD_DRIVER_DEBUG +#ifdef HANTRO_VCMD_DRIVER_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "vc8000_vcmd: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(__FILE__ ":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/*------------------------------------------------------------------------ +*****************************VCMD CONFIGURATION BY CUSTOMER******************************** +-------------------------------------------------------------------------*/ +//video encoder vcmd configuration + +#define VCMD_ENC_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_0 -1 +#define VCMD_ENC_MODULE_TYPE_0 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_ENC_IO_ADDR_1 0x91000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_1 -1 +#define VCMD_ENC_MODULE_TYPE_1 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_ENC_IO_ADDR_2 0x92000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_2 -1 +#define VCMD_ENC_MODULE_TYPE_2 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_ENC_IO_ADDR_3 0x93000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_3 -1 +#define VCMD_ENC_MODULE_TYPE_3 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_3 0XFFFF + +//video encoder cutree/IM vcmd configuration + + +#define VCMD_IM_IO_ADDR_0 0xa0000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_0 -1 +#define VCMD_IM_MODULE_TYPE_0 1 +#define VCMD_IM_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_IM_IO_ADDR_1 0xa1000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_1 -1 +#define VCMD_IM_MODULE_TYPE_1 1 +#define VCMD_IM_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_IM_IO_ADDR_2 0xa2000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_2 -1 +#define VCMD_IM_MODULE_TYPE_2 1 +#define VCMD_IM_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_IM_IO_ADDR_3 0xa3000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_3 -1 +#define VCMD_IM_MODULE_TYPE_3 1 +#define VCMD_IM_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_3 0XFFFF + +//video decoder vcmd configuration + + +#define VCMD_DEC_IO_ADDR_0 0x600000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_0 -1 +#define VCMD_DEC_MODULE_TYPE_0 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_0 0X2000 +#define VCMD_DEC_MODULE_MMU_ADDR_0 0XFFFF + + +#define VCMD_DEC_IO_ADDR_1 0x700000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_1 -1 +#define VCMD_DEC_MODULE_TYPE_1 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_1 0x1000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_1 0X2000 +#define VCMD_DEC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_DEC_IO_ADDR_2 0xb2000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_2 -1 +#define VCMD_DEC_MODULE_TYPE_2 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_DEC_IO_ADDR_3 0xb3000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_3 -1 +#define VCMD_DEC_MODULE_TYPE_3 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_3 0XFFFF + +//JPEG encoder vcmd configuration + +#define VCMD_JPEGE_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_0 -1 +#define VCMD_JPEGE_MODULE_TYPE_0 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_1 0xC1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_1 -1 +#define VCMD_JPEGE_MODULE_TYPE_1 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_2 0xC2000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_2 -1 +#define VCMD_JPEGE_MODULE_TYPE_2 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_3 0xC3000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_3 -1 +#define VCMD_JPEGE_MODULE_TYPE_3 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_3 0XFFFF + + +//JPEG decoder vcmd configuration + +#define VCMD_JPEGD_IO_ADDR_0 0x600000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_0 -1 +#define VCMD_JPEGD_MODULE_TYPE_0 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_1 0xD1000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_1 -1 +#define VCMD_JPEGD_MODULE_TYPE_1 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_2 0xD2000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_2 -1 +#define VCMD_JPEGD_MODULE_TYPE_2 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_3 0xD3000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_3 -1 +#define VCMD_JPEGD_MODULE_TYPE_3 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_3 0XFFFF + +/*for all vcmds, the core info should be listed here for subsequent use*/ +struct vcmd_config vcmd_core_array[MAX_SUBSYS_NUM]= { +#if 0 + //encoder configuration + {VCMD_ENC_IO_ADDR_0, + VCMD_ENC_IO_SIZE_0, + VCMD_ENC_INT_PIN_0, + VCMD_ENC_MODULE_TYPE_0, + VCMD_ENC_MODULE_MAIN_ADDR_0, + VCMD_ENC_MODULE_DEC400_ADDR_0, + VCMD_ENC_MODULE_L2CACHE_ADDR_0, + VCMD_ENC_MODULE_MMU_ADDR_0}, + + {VCMD_ENC_IO_ADDR_1, + VCMD_ENC_IO_SIZE_1, + VCMD_ENC_INT_PIN_1, + VCMD_ENC_MODULE_TYPE_1, + VCMD_ENC_MODULE_MAIN_ADDR_1, + VCMD_ENC_MODULE_DEC400_ADDR_1, + VCMD_ENC_MODULE_L2CACHE_ADDR_1, + VCMD_ENC_MODULE_MMU_ADDR_1}, + + {VCMD_ENC_IO_ADDR_2, + VCMD_ENC_IO_SIZE_2, + VCMD_ENC_INT_PIN_2, + VCMD_ENC_MODULE_TYPE_2, + VCMD_ENC_MODULE_MAIN_ADDR_2, + VCMD_ENC_MODULE_DEC400_ADDR_2, + VCMD_ENC_MODULE_L2CACHE_ADDR_2, + VCMD_ENC_MODULE_MMU_ADDR_2}, + + {VCMD_ENC_IO_ADDR_3, + VCMD_ENC_IO_SIZE_3, + VCMD_ENC_INT_PIN_3, + VCMD_ENC_MODULE_TYPE_3, + VCMD_ENC_MODULE_MAIN_ADDR_3, + VCMD_ENC_MODULE_DEC400_ADDR_3, + VCMD_ENC_MODULE_L2CACHE_ADDR_3, + VCMD_ENC_MODULE_MMU_ADDR_3}, + + //cutree/IM configuration + + {VCMD_IM_IO_ADDR_0, + VCMD_IM_IO_SIZE_0, + VCMD_IM_INT_PIN_0, + VCMD_IM_MODULE_TYPE_0, + VCMD_IM_MODULE_MAIN_ADDR_0, + VCMD_IM_MODULE_DEC400_ADDR_0, + VCMD_IM_MODULE_L2CACHE_ADDR_0, + VCMD_IM_MODULE_MMU_ADDR_0}, + + {VCMD_IM_IO_ADDR_1, + VCMD_IM_IO_SIZE_1, + VCMD_IM_INT_PIN_1, + VCMD_IM_MODULE_TYPE_1, + VCMD_IM_MODULE_MAIN_ADDR_1, + VCMD_IM_MODULE_DEC400_ADDR_1, + VCMD_IM_MODULE_L2CACHE_ADDR_1, + VCMD_IM_MODULE_MMU_ADDR_1}, + + {VCMD_IM_IO_ADDR_2, + VCMD_IM_IO_SIZE_2, + VCMD_IM_INT_PIN_2, + VCMD_IM_MODULE_TYPE_2, + VCMD_IM_MODULE_MAIN_ADDR_2, + VCMD_IM_MODULE_DEC400_ADDR_2, + VCMD_IM_MODULE_L2CACHE_ADDR_2, + VCMD_IM_MODULE_MMU_ADDR_2}, + + {VCMD_IM_IO_ADDR_3, + VCMD_IM_IO_SIZE_3, + VCMD_IM_INT_PIN_3, + VCMD_IM_MODULE_TYPE_3, + VCMD_IM_MODULE_MAIN_ADDR_3, + VCMD_IM_MODULE_DEC400_ADDR_3, + VCMD_IM_MODULE_L2CACHE_ADDR_3, + VCMD_IM_MODULE_MMU_ADDR_3}, +#endif + + //decoder configuration + {VCMD_DEC_IO_ADDR_0, + VCMD_DEC_IO_SIZE_0, + VCMD_DEC_INT_PIN_0, + VCMD_DEC_MODULE_TYPE_0, + VCMD_DEC_MODULE_MAIN_ADDR_0, + VCMD_DEC_MODULE_DEC400_ADDR_0, + VCMD_DEC_MODULE_L2CACHE_ADDR_0, + VCMD_DEC_MODULE_MMU_ADDR_0}, + + {VCMD_DEC_IO_ADDR_1, + VCMD_DEC_IO_SIZE_1, + VCMD_DEC_INT_PIN_1, + VCMD_DEC_MODULE_TYPE_1, + VCMD_DEC_MODULE_MAIN_ADDR_1, + VCMD_DEC_MODULE_DEC400_ADDR_1, + VCMD_DEC_MODULE_L2CACHE_ADDR_1, + VCMD_DEC_MODULE_MMU_ADDR_1}, + +#if 0 + {VCMD_DEC_IO_ADDR_2, + VCMD_DEC_IO_SIZE_2, + VCMD_DEC_INT_PIN_2, + VCMD_DEC_MODULE_TYPE_2, + VCMD_DEC_MODULE_MAIN_ADDR_2, + VCMD_DEC_MODULE_DEC400_ADDR_2, + VCMD_DEC_MODULE_L2CACHE_ADDR_2, + VCMD_DEC_MODULE_MMU_ADDR_2}, + + {VCMD_DEC_IO_ADDR_3, + VCMD_DEC_IO_SIZE_3, + VCMD_DEC_INT_PIN_3, + VCMD_DEC_MODULE_TYPE_3, + VCMD_DEC_MODULE_MAIN_ADDR_3, + VCMD_DEC_MODULE_DEC400_ADDR_3, + VCMD_DEC_MODULE_L2CACHE_ADDR_3, + VCMD_DEC_MODULE_MMU_ADDR_3}, +#endif +#if 0 + //JPEG encoder configuration + {VCMD_JPEGE_IO_ADDR_0, + VCMD_JPEGE_IO_SIZE_0, + VCMD_JPEGE_INT_PIN_0, + VCMD_JPEGE_MODULE_TYPE_0, + VCMD_JPEGE_MODULE_MAIN_ADDR_0, + VCMD_JPEGE_MODULE_DEC400_ADDR_0, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_0, + VCMD_JPEGE_MODULE_MMU_ADDR_0}, +#endif +#if 0 + {VCMD_JPEGE_IO_ADDR_1, + VCMD_JPEGE_IO_SIZE_1, + VCMD_JPEGE_INT_PIN_1, + VCMD_JPEGE_MODULE_TYPE_1, + VCMD_JPEGE_MODULE_MAIN_ADDR_1, + VCMD_JPEGE_MODULE_DEC400_ADDR_1, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGE_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGE_IO_ADDR_2, + VCMD_JPEGE_IO_SIZE_2, + VCMD_JPEGE_INT_PIN_2, + VCMD_JPEGE_MODULE_TYPE_2, + VCMD_JPEGE_MODULE_MAIN_ADDR_2, + VCMD_JPEGE_MODULE_DEC400_ADDR_2, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGE_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGE_IO_ADDR_3, + VCMD_JPEGE_IO_SIZE_3, + VCMD_JPEGE_INT_PIN_3, + VCMD_JPEGE_MODULE_TYPE_3, + VCMD_JPEGE_MODULE_MAIN_ADDR_3, + VCMD_JPEGE_MODULE_DEC400_ADDR_3, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGE_MODULE_MMU_ADDR_3}, + //JPEG decoder configuration + {VCMD_JPEGD_IO_ADDR_0, + VCMD_JPEGD_IO_SIZE_0, + VCMD_JPEGD_INT_PIN_0, + VCMD_JPEGD_MODULE_TYPE_0, + VCMD_JPEGD_MODULE_MAIN_ADDR_0, + VCMD_JPEGD_MODULE_DEC400_ADDR_0, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_0, + VCMD_JPEGD_MODULE_MMU_ADDR_0}, + + {VCMD_JPEGD_IO_ADDR_1, + VCMD_JPEGD_IO_SIZE_1, + VCMD_JPEGD_INT_PIN_1, + VCMD_JPEGD_MODULE_TYPE_1, + VCMD_JPEGD_MODULE_MAIN_ADDR_1, + VCMD_JPEGD_MODULE_DEC400_ADDR_1, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGD_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGD_IO_ADDR_2, + VCMD_JPEGD_IO_SIZE_2, + VCMD_JPEGD_INT_PIN_2, + VCMD_JPEGD_MODULE_TYPE_2, + VCMD_JPEGD_MODULE_MAIN_ADDR_2, + VCMD_JPEGD_MODULE_DEC400_ADDR_2, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGD_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGD_IO_ADDR_3, + VCMD_JPEGD_IO_SIZE_3, + VCMD_JPEGD_INT_PIN_3, + VCMD_JPEGD_MODULE_TYPE_3, + VCMD_JPEGD_MODULE_MAIN_ADDR_3, + VCMD_JPEGD_MODULE_DEC400_ADDR_3, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGD_MODULE_MMU_ADDR_3}, +#endif +}; +/*these size need to be modified according to hw config.*/ +#define VCMD_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_DECODER_REGISTER_SIZE (1023 * 4) +#define VCMD_IM_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_DECODER_REGISTER_SIZE (1023 * 4) + +#define MAX_VCMD_NUMBER (MAX_VCMD_TYPE*MAX_SAME_MODULE_TYPE_CORE_NUMBER) // + + +#define HW_WORK_STATE_PEND 3 + +#define MAX_CMDBUF_INT_NUMBER 1 +#define INT_MIN_SUM_OF_IMAGE_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_CMDBUF_INT_NUMBER) +#define MAX_PROCESS_CORE_NUMBER 4*8 +#define PROCESS_MAX_VIDEO_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_PROCESS_CORE_NUMBER) +#define PROCESS_MAX_JPEG_SIZE (2147483648) //32768*32768*2 +#define PROCESS_MAX_SUM_OF_IMAGE_SIZE (PROCESS_MAX_VIDEO_SIZE>PROCESS_MAX_JPEG_SIZE?PROCESS_MAX_VIDEO_SIZE:PROCESS_MAX_JPEG_SIZE) + + +#define MAX_SAME_MODULE_TYPE_CORE_NUMBER 4 + + + +/*******************PCIE CONFIG*************************/ + +/*******************PCIE CONFIG*************************/ + +#define PCI_VENDOR_ID_HANTRO 0x10ee//0x16c3 +#define PCI_DEVICE_ID_HANTRO_PCI 0x8014// 0x7011 + +/* Base address got control register */ +#define PCI_H2_BAR 4 + +/* Base address DDR register */ +#define PCI_DDR_BAR 0 + + +static size_t base_ddr_addr = 0; /*pcie address need to substract this value then can be put to register*/ + + + +/********variables declaration related with race condition**********/ + +#define CMDBUF_MAX_SIZE (512*4*4) + + +#define CMDBUF_POOL_TOTAL_SIZE (2*1024*1024) //approximately=128x(320x240)=128x2k=128x8kbyte=1Mbytes +#define TOTAL_DISCRETE_CMDBUF_NUM (CMDBUF_POOL_TOTAL_SIZE/CMDBUF_MAX_SIZE) +#define CMDBUF_VCMD_REGISTER_TOTAL_SIZE 9*1024*1024-CMDBUF_POOL_TOTAL_SIZE*2 +#define VCMD_REGISTER_SIZE (128*4) + +#ifndef DYNAMIC_MALLOC_VCMDNODE +static struct cmdbuf_obj *g_cmdbuf_obj_pool; +static struct bi_list_node *g_cmdbuf_node_pool; +#endif + +struct noncache_mem +{ + u32 *virtualAddress; + dma_addr_t busAddress; + unsigned int mmu_bus_address; /* buffer physical address in MMU*/ + u32 size; + u16 cmdbuf_id; +}; + +struct process_manager_obj +{ + struct file *filp; + u64 total_exe_time; + spinlock_t spinlock; + u32 pm_count; + wait_queue_head_t wait_queue; +} ; + +struct cmdbuf_obj +{ + u32 module_type; //current CMDBUF type: input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4 + u32 priority; //current CMDBUFpriority: normal=0, high=1 + u64 executing_time; //current CMDBUFexecuting_time=encoded_image_size*(rdoLevel+1)*(rdoq+1); + u32 cmdbuf_size; //current CMDBUF size + u32 *cmdbuf_virtualAddress; //current CMDBUF start virtual address. + size_t cmdbuf_busAddress; //current CMDBUF start physical address. + unsigned int mmu_cmdbuf_busAddress; //current CMDBUF start mmu mapping address. + u32 *status_virtualAddress; //current status CMDBUF start virtual address. + size_t status_busAddress; //current status CMDBUF start physical address. + unsigned int mmu_status_busAddress; //current status CMDBUF start mmu mapping address. + u32 status_size; //current status CMDBUF size + u32 executing_status; //current CMDBUF executing status. + struct file *filp; //file pointer in the same process. + u16 core_id; //which vcmd core is used. + u16 cmdbuf_id; //used to manage CMDBUF in driver.It is a handle to identify cmdbuf.also is an interrupt vector.position in pool,same as status position. + u8 cmdbuf_data_loaded; //0 means sw has not copied data into this CMDBUF; 1 means sw has copied data into this CMDBUF + u8 cmdbuf_data_linked; //0 :not linked, 1:linked. + u8 cmdbuf_run_done; //if 0,waiting for CMDBUF finish; if 1, op code in CMDBUF has finished one by one. HANTRO_VCMD_IOCH_WAIT_CMDBUF will check this variable. + u8 cmdbuf_need_remove; // if 0, not need to remove CMDBUF; 1 CMDBUF can be removed if it is not the last CMDBUF; + u32 waited; // if 0, the cmd buf hasn't been waited, otherwise, has been waited. + u8 has_end_cmdbuf; //if 1, the last opcode is end opCode. + u8 no_normal_int_cmdbuf; //if 1, JMP will not send normal interrupt. + struct process_manager_obj* process_manager_obj; +}; + +struct hantrovcmd_dev +{ + struct vcmd_config vcmd_core_cfg; //config of each core,such as base addr, irq,etc + u32 core_id; //vcmd core id for driver and sw internal use + u32 sw_cmdbuf_rdy_num; + spinlock_t* spinlock; + wait_queue_head_t * wait_queue; + wait_queue_head_t * wait_abort_queue; + bi_list list_manager; + volatile u8 *hwregs;/* IO mem base */ + u32 reg_mirror[ASIC_VCMD_SWREG_AMOUNT]; + u32 duration_without_int; //number of cmdbufs without interrupt. + volatile u8 working_state; + u64 total_exe_time; + u16 status_cmdbuf_id;//used for analyse configuration in cwl. + u32 hw_version_id; /*megvii 0x43421001, later 0x43421102*/ + u32 *vcmd_reg_mem_virtualAddress;//start virtual address of vcmd registers memory of CMDBUF. + size_t vcmd_reg_mem_busAddress; //start physical address of vcmd registers memory of CMDBUF. + unsigned int mmu_vcmd_reg_mem_busAddress; //start mmu mapping address of vcmd registers memory of CMDBUF. + u32 vcmd_reg_mem_size; // size of vcmd registers memory of CMDBUF. + struct platform_device *pdev; +} ; + +/* + * Ioctl definitions + */ +#define VCMD_HW_ID 0x4342 + + +static struct noncache_mem vcmd_buf_mem_pool; +static struct noncache_mem vcmd_status_buf_mem_pool; +static struct noncache_mem vcmd_registers_mem_pool; + +static u16 cmdbuf_used[TOTAL_DISCRETE_CMDBUF_NUM]; +static u16 cmdbuf_used_pos; +static u16 cmdbuf_used_residual; + +static struct hantrovcmd_dev* vcmd_manager[MAX_VCMD_TYPE][MAX_VCMD_NUMBER]; +bi_list_node* global_cmdbuf_node[TOTAL_DISCRETE_CMDBUF_NUM]; + +bi_list global_process_manager; + +static u16 vcmd_position[MAX_VCMD_TYPE]; +static int vcmd_type_core_num[MAX_VCMD_TYPE]; + +#define EXECUTING_CMDBUF_ID_ADDR 26 +#define VCMD_EXE_CMDBUF_COUNT 3 + +#define WORKING_STATE_IDLE 0 +#define WORKING_STATE_WORKING 1 +#define CMDBUF_EXE_STATUS_OK 0 +#define CMDBUF_EXE_STATUS_CMDERR 1 +#define CMDBUF_EXE_STATUS_BUSERR 2 + + +struct semaphore vcmd_reserve_cmdbuf_sem[MAX_VCMD_TYPE]; //for reserve + +extern unsigned long gBaseDDRHw; /* PCI base register address (memalloc) */ +extern unsigned int mmu_enable; + + +/***************************TYPE AND FUNCTION DECLARATION****************/ + +/* here's all the must remember stuff */ + + +static int vcmd_reserve_IO(void); +static void vcmd_release_IO(void); +static void vcmd_reset_asic(struct hantrovcmd_dev * dev); +static void vcmd_reset_current_asic(struct hantrovcmd_dev * dev); +static int allocate_cmdbuf(struct noncache_mem* new_cmdbuf_addr,struct noncache_mem* new_status_cmdbuf_addr); +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node); +static void vcmd_start(struct hantrovcmd_dev *dev,bi_list_node* first_linked_cmdbuf_node); +static void create_kernel_process_manager(void); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static irqreturn_t hantrovcmd_isr(int irq, void *dev_id, struct pt_regs *regs); +#else +static irqreturn_t hantrovcmd_isr(int irq, void *dev_id); +#endif + +static void printk_vcmd_register_debug(const void *hwregs, char * info); + + +/*********************local variable declaration*****************/ +/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ +int total_vcmd_core_num = 0; +/* dynamic allocation*/ +static struct hantrovcmd_dev* hantrovcmd_data = NULL; + +//#define VCMD_DEBUG_INTERNAL + +//#define IRQ_SIMULATION + +#ifdef IRQ_SIMULATION +struct timer_manager +{ + u32 core_id; //vcmd core id for driver and sw internal use + u32 timer_id; + struct timer_list *timer; +} ; + +static struct timer_list timer[10000]; +struct timer_manager timer_reserve[10000]; +#if 0 +static struct timer_list timer0; +static struct timer_list timer1; +#endif +#endif + + +//hw_queue can be used for reserve cmdbuf memory +DECLARE_WAIT_QUEUE_HEAD(vcmd_cmdbuf_memory_wait); + +DEFINE_SPINLOCK(vcmd_cmdbuf_alloc_lock); +DEFINE_SPINLOCK(vcmd_process_manager_lock); + + +spinlock_t owner_lock_vcmd[MAX_VCMD_NUMBER]; + +wait_queue_head_t wait_queue_vcmd[MAX_VCMD_NUMBER]; + +wait_queue_head_t abort_queue_vcmd[MAX_VCMD_NUMBER]; + +/* mc wait queue, used in wait_cmdbuf_ready with ANY_CMDBUF_ID. */ +static wait_queue_head_t mc_wait_queue; + +#if 0 +/*allocate non-cacheable DMA memory*/ +#define DRIVER_NAME_HANTRO_NON_CACH_MEM "non_cach_memory" + +static struct platform_device *noncachable_mem_dev = NULL; + + +static const struct platform_device_info hantro_platform_info = { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + + +static int hantro_noncachable_mem_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + vcmd_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_status_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_status_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + return 0; +} + +static int hantro_noncachable_mem_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dma_free_coherent(dev,vcmd_buf_mem_pool.size,vcmd_buf_mem_pool.virtualAddress,vcmd_buf_mem_pool.busAddress); + dma_free_coherent(dev,vcmd_status_buf_mem_pool.size,vcmd_status_buf_mem_pool.virtualAddress,vcmd_status_buf_mem_pool.busAddress); + + return 0; +} + + +static const struct platform_device_id hantro_noncachable_mem_platform_ids[]={ + { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + }, + {/* sentinel */}, +}; + + +static const struct of_device_id hantro_of_match[]={ + { + .compatible = "thead,light-vc8000d", + }, + {/* sentinel */}, +}; + +static struct platform_driver hantro_noncachable_mem_platform_driver = { + .probe = hantro_noncachable_mem_probe, + .remove = hantro_noncachable_mem_remove, + .driver ={ + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .owner = THIS_MODULE, + .of_match_table = hantro_of_match, + }, + .id_table = hantro_noncachable_mem_platform_ids, +}; + +static void init_vcmd_non_cachable_memory_allocate(void) +{ + + /*create device: This will create a {struct platform_device}, It has a member dev, which is a {struct device} */ + noncachable_mem_dev = platform_device_register_full(&hantro_platform_info); + + /*when this function is called, the .probe callback is invoked.*/ + platform_driver_register(&hantro_noncachable_mem_platform_driver); + + +} + +static void release_vcmd_non_cachable_memory(void) +{ + + /* when this fucntion is called, .remove callback will be invoked. use it to clean up all resources allocated in .probe.*/ + platform_driver_unregister(&hantro_noncachable_mem_platform_driver); + + /*destroy the device*/ + platform_device_unregister(noncachable_mem_dev); +} +#endif + +void PrintInstr(u32 i, u32 instr, u32 *size) { + if ((instr & 0xF8000000) == OPCODE_WREG) { + int length = ((instr >> 16) & 0x3FF); + pr_info("current cmdbuf data %d = 0x%08x => [%s %s %d 0x%x]\n", i, instr, "WREG", ((instr >> 26) & 0x1) ? "FIX" : "", + length, (instr & 0xFFFF)); + *size = ((length+2)>>1)<<1; + } else if ((instr & 0xF8000000) == OPCODE_END) { + pr_info("current cmdbuf data %d = 0x%08x => [%s]\n", i, instr, "END"); + *size = 2; + } else if ((instr & 0xF8000000) == OPCODE_NOP) { + pr_info("current cmdbuf data %d = 0x%08x => [%s]\n", i, instr, "NOP"); + *size = 2; + } else if ((instr & 0xF8000000) == OPCODE_RREG) { + int length = ((instr >> 16) & 0x3FF); + pr_info("current cmdbuf data %d = 0x%08x => [%s %s %d 0x%x]\n", i, instr, "RREG", ((instr >> 26) & 0x1) ? "FIX" : "", + length, (instr & 0xFFFF)); + *size = 4; + } else if ((instr & 0xF8000000) == OPCODE_JMP) { + pr_info("current cmdbuf data %d = 0x%08x => [%s %s %s]\n", i, instr, "JMP", ((instr >> 26) & 0x1) ? "RDY" : "", + ((instr >> 25) & 0x1) ? "IE" : ""); + *size = 4; + } else if ((instr & 0xF8000000) == OPCODE_STALL) { + pr_info("current cmdbuf data %d = 0x%08x => [%s %s 0x%x]\n", i, instr, "STALL", ((instr >> 26) & 0x1) ? "IM" : "", + (instr & 0xFFFF)); + *size = 2; + } else if ((instr & 0xF8000000) == OPCODE_CLRINT) { + pr_info("current cmdbuf data %d = 0x%08x => [%s %d 0x%x]\n", i, instr, "CLRINT", (instr >> 25) & 0x3, + (instr & 0xFFFF)); + *size = 2; + } else + *size = 1; +} + +/**********************************************************************************************************\ +*cmdbuf object management +\***********************************************************************************************************/ +#ifdef DYNAMIC_MALLOC_VCMDNODE +static struct cmdbuf_obj* create_cmdbuf_obj(void) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + cmdbuf_obj=vmalloc(sizeof(struct cmdbuf_obj)); + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","vmalloc for cmdbuf_obj fail!"); + return cmdbuf_obj; + } + memset(cmdbuf_obj,0,sizeof(struct cmdbuf_obj)); + return cmdbuf_obj; +} +#endif + +#ifdef DYNAMIC_MALLOC_VCMDNODE +static void free_cmdbuf_obj(struct cmdbuf_obj* cmdbuf_obj) +{ + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(cmdbuf_obj); + return; +} +#endif + +static void free_cmdbuf_mem(u16 cmdbuf_id ) +{ + unsigned long flags; + + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + cmdbuf_used[cmdbuf_id]=0; + cmdbuf_used_residual +=1; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); +} + +static bi_list_node* create_cmdbuf_node(void) +{ + bi_list_node* current_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + struct noncache_mem new_cmdbuf_addr; + struct noncache_mem new_status_cmdbuf_addr; + + if(wait_event_interruptible(vcmd_cmdbuf_memory_wait, allocate_cmdbuf(&new_cmdbuf_addr,&new_status_cmdbuf_addr)) ) + return NULL; +#ifdef DYNAMIC_MALLOC_VCMDNODE + cmdbuf_obj=create_cmdbuf_obj(); +#else + cmdbuf_obj = g_cmdbuf_obj_pool + new_cmdbuf_addr.cmdbuf_id; + if (cmdbuf_obj) + memset(cmdbuf_obj, 0, sizeof(struct cmdbuf_obj)); +#endif + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","create_cmdbuf_obj fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); + return NULL; + } + cmdbuf_obj->cmdbuf_busAddress = new_cmdbuf_addr.busAddress; + cmdbuf_obj->mmu_cmdbuf_busAddress = new_cmdbuf_addr.mmu_bus_address; + cmdbuf_obj->cmdbuf_virtualAddress = new_cmdbuf_addr.virtualAddress; + cmdbuf_obj->cmdbuf_size = new_cmdbuf_addr.size; + cmdbuf_obj->cmdbuf_id = new_cmdbuf_addr.cmdbuf_id; + cmdbuf_obj->status_busAddress = new_status_cmdbuf_addr.busAddress; + cmdbuf_obj->mmu_status_busAddress = new_status_cmdbuf_addr.mmu_bus_address; + cmdbuf_obj->status_virtualAddress = new_status_cmdbuf_addr.virtualAddress; + cmdbuf_obj->status_size = new_status_cmdbuf_addr.size; +#ifdef DYNAMIC_MALLOC_VCMDNODE + current_node=bi_list_create_node(); +#else + current_node = g_cmdbuf_node_pool + cmdbuf_obj->cmdbuf_id; + if (current_node) + memset(current_node, 0, sizeof(bi_list_node)); +#endif + if(current_node==NULL) + { + PDEBUG ("%s\n","bi_list_create_node fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); +#ifdef DYNAMIC_MALLOC_VCMDNODE + free_cmdbuf_obj(cmdbuf_obj); +#endif + return NULL; + } + current_node->data = (void*)cmdbuf_obj; + current_node->next = NULL; + current_node->previous = NULL; + return current_node; +} +static void free_cmdbuf_node(bi_list_node* cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + if(cmdbuf_node==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_node NULL"); + return; + } + cmdbuf_obj = (struct cmdbuf_obj*)cmdbuf_node->data; + //free cmdbuf mem in pool + free_cmdbuf_mem(cmdbuf_obj->cmdbuf_id); + +#ifdef DYNAMIC_MALLOC_VCMDNODE + //free struct cmdbuf_obj + free_cmdbuf_obj(cmdbuf_obj); + //free current cmdbuf_node entity. + bi_list_free_node(cmdbuf_node); +#endif + return; +} + +//just remove, not free the node. +static bi_list_node* remove_cmdbuf_node_from_list(bi_list* list,bi_list_node* cmdbuf_node) +{ + if(cmdbuf_node==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_node_from_list NULL"); + return NULL; + } + if(cmdbuf_node->next) + { + bi_list_remove_node(list,cmdbuf_node); + return cmdbuf_node; + } + else + { + //the last one, should not be removed. + return NULL; + } +} +//calculate executing_time of each vcmd +static u64 calculate_executing_time_after_node(bi_list_node* exe_cmdbuf_node) +{ + u64 time_run_all=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + while(1) + { + if(exe_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} +static u64 calculate_executing_time_after_node_high_priority(bi_list_node* exe_cmdbuf_node) +{ + u64 time_run_all=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + if(exe_cmdbuf_node==NULL) + return time_run_all; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + while(1) + { + if(exe_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + if(cmdbuf_obj_temp->priority==CMDBUF_PRIORITY_NORMAL) + break; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} + + +/**********************************************************************************************************\ +*cmdbuf pool management +\***********************************************************************************************************/ +static int allocate_cmdbuf(struct noncache_mem* new_cmdbuf_addr,struct noncache_mem* new_status_cmdbuf_addr) +{ + unsigned long flags; + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + if(cmdbuf_used_residual==0) + { + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + //no empty cmdbuf + return 0; + } + //there is one cmdbuf at least + while(1) + { + if(cmdbuf_used[cmdbuf_used_pos]==0&&(global_cmdbuf_node[cmdbuf_used_pos]==NULL )) + { + cmdbuf_used[cmdbuf_used_pos]=1; + cmdbuf_used_residual -=1; + new_cmdbuf_addr->virtualAddress=vcmd_buf_mem_pool.virtualAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE/4; + new_cmdbuf_addr->busAddress=vcmd_buf_mem_pool.busAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_cmdbuf_addr->mmu_bus_address=vcmd_buf_mem_pool.mmu_bus_address + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_cmdbuf_addr->size=CMDBUF_MAX_SIZE; + new_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + new_status_cmdbuf_addr->virtualAddress=vcmd_status_buf_mem_pool.virtualAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE/4; + new_status_cmdbuf_addr->busAddress=vcmd_status_buf_mem_pool.busAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->mmu_bus_address=vcmd_status_buf_mem_pool.mmu_bus_address + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->size=CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + cmdbuf_used_pos++; + if(cmdbuf_used_pos>=TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos=0; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + return 1; + } + else + { + cmdbuf_used_pos++; + if(cmdbuf_used_pos>=TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos=0; + } + } + return 0; +} + +static bi_list_node* get_cmdbuf_node_in_list_by_addr(size_t cmdbuf_addr,bi_list* list) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + new_cmdbuf_node=list->head; + while(1) + { + if(new_cmdbuf_node==NULL) + return NULL; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=cmdbuf_addr)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >cmdbuf_addr)) ) + { + return new_cmdbuf_node; + } + new_cmdbuf_node=new_cmdbuf_node->next; + } + return NULL; +} + +static int wait_abort_rdy(struct hantrovcmd_dev*dev) +{ + return dev->working_state == WORKING_STATE_IDLE; +} +static int select_vcmd(bi_list_node* new_cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* curr_cmdbuf_node=NULL; + bi_list* list=NULL; + struct hantrovcmd_dev*dev=NULL; + struct hantrovcmd_dev*smallest_dev=NULL; + u64 executing_time=0xffffffffffffffff; + int counter=0; + unsigned long flags=0; + u32 hw_rdy_cmdbuf_num=0; + size_t exe_cmdbuf_addr=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + u32 cmdbuf_id=0; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + //there is an empty vcmd to be used + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + if( list->tail==NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //there is a vcmd which tail node -> cmdbuf_run_done == 1. It means this vcmd has nothing to do, so we select it. + counter =0; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + curr_cmdbuf_node = list->tail; + if(curr_cmdbuf_node == NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_run_done ==1) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + + //another case, tail = executing node, and vcmd=pend state (finish but not generate interrupt) + counter =0; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + //read executing cmdbuf address + if(dev->hw_version_id <= HW_ID_1_0_C ) + hw_rdy_cmdbuf_num = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT); + else + { + hw_rdy_cmdbuf_num = *(dev->vcmd_reg_mem_virtualAddress+VCMD_EXE_CMDBUF_COUNT); + if(hw_rdy_cmdbuf_num!=dev->sw_cmdbuf_rdy_num) + hw_rdy_cmdbuf_num += 1; + } + spin_lock_irqsave(dev->spinlock, flags); + curr_cmdbuf_node = list->tail; + if(curr_cmdbuf_node == NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + + if((dev->sw_cmdbuf_rdy_num ==hw_rdy_cmdbuf_num)) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + + + + //there is no idle vcmd,if low priority,calculate exe time, select the least one. + // or if high priority, calculate the exe time, select the least one and abort it. + if(cmdbuf_obj->priority==CMDBUF_PRIORITY_NORMAL) + { + + counter =0; + //calculate total execute time of all devices + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + //read executing cmdbuf address + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + exe_cmdbuf_addr = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node=get_cmdbuf_node_in_list_by_addr(exe_cmdbuf_addr,list); + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + else + { + //cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR+1); + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM||cmdbuf_id == 0) + { + pr_err("cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return -1; + } + //get the executing cmdbuf node. + curr_cmdbuf_node=global_cmdbuf_node[cmdbuf_id]; + if(curr_cmdbuf_node==NULL) + { + list=&dev->list_manager; + curr_cmdbuf_node = list->head; + while(1) + { + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_data_linked&&cmdbuf_obj_temp->cmdbuf_run_done==0) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + + } + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //find the device with the least total_exe_time. + counter =0; + executing_time=0xffffffffffffffff; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->total_exe_time <= executing_time) + { + executing_time = dev->total_exe_time; + smallest_dev = dev; + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //insert list + list = &smallest_dev->list_manager; + spin_lock_irqsave(smallest_dev->spinlock, flags); + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + cmdbuf_obj->core_id = smallest_dev->core_id; + return 0; + } + else + { + //CMDBUF_PRIORITY_HIGH + counter =0; + //calculate total execute time of all devices + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + //read executing cmdbuf address + exe_cmdbuf_addr = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node=get_cmdbuf_node_in_list_by_addr(exe_cmdbuf_addr,list); + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node_high_priority(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + else + { + //cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR); + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM||cmdbuf_id == 0) + { + pr_err("cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return -1; + } + //get the executing cmdbuf node. + curr_cmdbuf_node=global_cmdbuf_node[cmdbuf_id]; + if(curr_cmdbuf_node==NULL) + { + list=&dev->list_manager; + curr_cmdbuf_node = list->head; + while(1) + { + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_data_linked&&cmdbuf_obj_temp->cmdbuf_run_done==0) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + } + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //find the smallest device. + counter =0; + executing_time=0xffffffffffffffff; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->total_exe_time <= executing_time) + { + executing_time = dev->total_exe_time; + smallest_dev = dev; + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //abort the vcmd and wait + vcmd_write_register_value((const void *)smallest_dev->hwregs,smallest_dev->reg_mirror,HWIF_VCMD_START_TRIGGER,0); + if(wait_event_interruptible(*smallest_dev->wait_abort_queue, wait_abort_rdy(smallest_dev)) ) + return -ERESTARTSYS; + //need to select inserting position again because hw maybe have run to the next node. + //CMDBUF_PRIORITY_HIGH + spin_lock_irqsave(smallest_dev->spinlock, flags); + curr_cmdbuf_node = smallest_dev->list_manager.head; + while(1) + { + //if list is empty or tail,insert to tail + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp= (struct cmdbuf_obj*)curr_cmdbuf_node->data; + //if find the first node which priority is normal, insert node prior to the node + if((cmdbuf_obj_temp->priority==CMDBUF_PRIORITY_NORMAL) && (cmdbuf_obj_temp->cmdbuf_run_done==0)) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + bi_list_insert_node_before(list,curr_cmdbuf_node,new_cmdbuf_node); + cmdbuf_obj->core_id = smallest_dev->core_id; + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + + return 0; + } + return 0; +} +static int wait_process_resource_rdy(struct process_manager_obj* process_manager_obj ) +{ + return process_manager_obj->total_exe_time<=PROCESS_MAX_SUM_OF_IMAGE_SIZE; +} + +static long reserve_cmdbuf(struct file *filp,struct exchange_parameter* input_para) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* process_manager_node=NULL; + struct process_manager_obj* process_manager_obj=NULL; + unsigned long flags; + input_para->cmdbuf_id = 0; + if(input_para->cmdbuf_size>CMDBUF_MAX_SIZE) + { + return -1; + } + PDEBUG("reserve cmdbuf filp %p\n", (void *)filp); + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while(1) + { + if (process_manager_node == NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR process_manager_node !!\n"); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + return -1; + } + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + PDEBUG("reserve loop: node %p, filp %p\n", (void *)process_manager_node, + (void *)process_manager_obj->filp); + if (filp == process_manager_obj->filp) + break; + + process_manager_node = process_manager_node->next; + } + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + spin_lock_irqsave(&process_manager_obj->spinlock, flags); + process_manager_obj->total_exe_time += input_para->executing_time; + spin_unlock_irqrestore(&process_manager_obj->spinlock, flags); + if(wait_event_interruptible(process_manager_obj->wait_queue, wait_process_resource_rdy(process_manager_obj))) + return -1; + + new_cmdbuf_node=create_cmdbuf_node(); + if(new_cmdbuf_node==NULL) + return -1; + + cmdbuf_obj = (struct cmdbuf_obj* )new_cmdbuf_node->data; + cmdbuf_obj->module_type = input_para->module_type; + cmdbuf_obj->priority = input_para->priority; + cmdbuf_obj->executing_time = input_para->executing_time; + cmdbuf_obj->cmdbuf_size = CMDBUF_MAX_SIZE; + input_para->cmdbuf_size =CMDBUF_MAX_SIZE; + cmdbuf_obj->filp = filp; + cmdbuf_obj->process_manager_obj =process_manager_obj; + + input_para->cmdbuf_id=cmdbuf_obj->cmdbuf_id; + global_cmdbuf_node[input_para->cmdbuf_id] = new_cmdbuf_node; + + return 0; +} + +static long release_cmdbuf(struct file *filp,u16 cmdbuf_id) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* last_cmdbuf_node=NULL; + bi_list_node* new_cmdbuf_node=NULL; + bi_list* list=NULL; + u32 module_type; + + + unsigned long flags; + struct hantrovcmd_dev* dev=NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + module_type = cmdbuf_obj->module_type; + //TODO + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[module_type])) + return -ERESTARTSYS; + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + + //spin_lock_irqsave(dev->spinlock, flags); + list=&dev->list_manager; + cmdbuf_obj->cmdbuf_need_remove=1; + last_cmdbuf_node = new_cmdbuf_node->previous; + while(1) + { + //remove current node + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_need_remove==1) + { + new_cmdbuf_node=remove_cmdbuf_node_from_list(list,new_cmdbuf_node); + if(new_cmdbuf_node) + { + //free node + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + if(cmdbuf_obj->process_manager_obj) + { + spin_lock_irqsave(&cmdbuf_obj->process_manager_obj->spinlock, flags); + cmdbuf_obj->process_manager_obj->total_exe_time -= cmdbuf_obj->executing_time; + spin_unlock_irqrestore(&cmdbuf_obj->process_manager_obj->spinlock, flags); + wake_up_interruptible_all(&cmdbuf_obj->process_manager_obj->wait_queue); + } + free_cmdbuf_node(new_cmdbuf_node); + + } + } + if(last_cmdbuf_node==NULL) + break; + new_cmdbuf_node=last_cmdbuf_node; + last_cmdbuf_node=new_cmdbuf_node->previous; + } + //spin_unlock_irqrestore(dev->spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[module_type]); + return 0; +} +static long release_cmdbuf_node(bi_list* list,bi_list_node*cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node=cmdbuf_node; + if(new_cmdbuf_node==NULL) + return -1; + //remove node from list + new_cmdbuf_node=remove_cmdbuf_node_from_list(list,new_cmdbuf_node); + if(new_cmdbuf_node) + { + //free node + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + return 0; + } + return 1; +} + +static long release_cmdbuf_node_cleanup(bi_list* list) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + while(1) + { + new_cmdbuf_node=list->head; + if(new_cmdbuf_node==NULL) + return 0; + //remove node from list + bi_list_remove_node(list,new_cmdbuf_node); + //free node + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + } + return 0; +} + + + +static bi_list_node* find_last_linked_cmdbuf(bi_list_node* current_node) +{ + bi_list_node* new_cmdbuf_node=current_node; + bi_list_node* last_cmdbuf_node; + struct cmdbuf_obj* cmdbuf_obj=NULL; + if(current_node==NULL) + return NULL; + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + while(1) + { + if(new_cmdbuf_node==NULL) + return last_cmdbuf_node; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_data_linked) + { + return new_cmdbuf_node; + } + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + return NULL; +} +static long link_and_run_cmdbuf(struct file *filp,struct exchange_parameter* input_para) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* new_cmdbuf_node=NULL; + bi_list_node* last_cmdbuf_node; + u32* jmp_addr=NULL; + u32 opCode; + u32 tempOpcode; + u32 record_last_cmdbuf_rdy_num; + struct hantrovcmd_dev* dev=NULL; + unsigned long flags; + int return_value; + u16 cmdbuf_id=input_para->cmdbuf_id; + + + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj->cmdbuf_data_loaded=1; + cmdbuf_obj->cmdbuf_size=input_para->cmdbuf_size; + cmdbuf_obj->waited = 0; +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, inst = 0, size = 0; + pr_info("vcmd link, current cmdbuf content\n"); + for(i=0;icmdbuf_size/4;i++) + { + if (i == inst) { + PrintInstr(i, *(cmdbuf_obj->cmdbuf_virtualAddress+i), &size); + inst += size; + } else { + pr_info("current cmdbuf data %d = 0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } + } +#endif + //test nop and end opcode, then assign value. + cmdbuf_obj->has_end_cmdbuf=0; //0: has jmp opcode,1 has end code + cmdbuf_obj->no_normal_int_cmdbuf=0; //0: interrupt when JMP,1 not interrupt when JMP + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + opCode=tempOpcode = *(jmp_addr-4); + opCode >>=27; + opCode <<=27; + //we can't identify END opcode or JMP opcode, so we don't support END opcode in control sw and driver. + if(opCode == OPCODE_JMP) + { + //jmp + opCode=tempOpcode; + opCode &=0x02000000; + if(opCode == JMP_IE_1) + { + cmdbuf_obj->no_normal_int_cmdbuf=0; + } + else + { + cmdbuf_obj->no_normal_int_cmdbuf=1; + } + } + else + { + //not support other opcode + return -1; + } + + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type])) + return -ERESTARTSYS; + + return_value=select_vcmd(new_cmdbuf_node); + if(return_value) + return return_value; + + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + input_para->core_id = cmdbuf_obj->core_id; + pr_debug("filp=%p, VCMD Link CMDBUF [%d] to core [%d]\n", (void *)filp, cmdbuf_id, input_para->core_id); + //set ddr address for vcmd registers copy. + if(dev->hw_version_id > HW_ID_1_0_C ) + { + //read vcmd executing register into ddr memory. + //now core id is got and output ddr address of vcmd register can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress; + if (mmu_enable) { + *(jmp_addr + 2) = 0; + *(jmp_addr+1) = (u32)((dev->mmu_vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr + 2) = (u32)((u64)(dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)>>32); + } else { + *(jmp_addr + 2) = 0; + } + *(jmp_addr+1) = (u32)((dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)); + } + + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + //read vcmd all registers into ddr memory. + //now core id is got and output ddr address of vcmd registers can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-6) = 0; + } + *(jmp_addr-7) = (u32)(dev->mmu_vcmd_reg_mem_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-6) = (u32)((u64)dev->vcmd_reg_mem_busAddress>>32); + } else { + *(jmp_addr-6) = 0; + } + *(jmp_addr-7) = (u32)(dev->vcmd_reg_mem_busAddress); + } + } + //start to link and/or run + spin_lock_irqsave(dev->spinlock, flags); + last_cmdbuf_node = find_last_linked_cmdbuf(new_cmdbuf_node); + record_last_cmdbuf_rdy_num=dev->sw_cmdbuf_rdy_num; + vcmd_link_cmdbuf(dev,last_cmdbuf_node); + if(dev->working_state==WORKING_STATE_IDLE) + { + //run + while (last_cmdbuf_node && + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_run_done) + last_cmdbuf_node = last_cmdbuf_node->next; + + if (last_cmdbuf_node && last_cmdbuf_node->data) { + PDEBUG("vcmd start for cmdbuf id %d, cmdbuf_run_done = %d\n", + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_id, + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_run_done); + } + vcmd_start(dev,last_cmdbuf_node); + } + else + { + //just update cmdbuf ready number + if(record_last_cmdbuf_rdy_num!=dev->sw_cmdbuf_rdy_num) + vcmd_write_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev->sw_cmdbuf_rdy_num); + } + spin_unlock_irqrestore(dev->spinlock, flags); + + up(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type]); + + + return 0; +} + +/******************************************************************************/ +static int check_cmdbuf_irq(struct hantrovcmd_dev* dev,struct cmdbuf_obj* cmdbuf_obj,u32 *irq_status_ret) +{ + + int rdy = 0; + unsigned long flags; + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_obj->cmdbuf_run_done) + { + rdy = 1; + *irq_status_ret=cmdbuf_obj->executing_status;//need to decide how to assign this variable + } + spin_unlock_irqrestore(dev->spinlock, flags); + return rdy; +} + +/******************************************************************************/ +static int check_mc_cmdbuf_irq(struct file *filp,struct cmdbuf_obj* cmdbuf_obj,u32 *irq_status_ret) +{ + int k; + bi_list_node* new_cmdbuf_node=NULL; + struct hantrovcmd_dev* dev=NULL; + + for(k=0;kdata; + if(!cmdbuf_obj || cmdbuf_obj->filp != filp) + continue; + + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + if (check_cmdbuf_irq(dev, cmdbuf_obj, irq_status_ret) == 1) { + /* Return cmdbuf_id when ANY_CMDBUF_ID is used. */ + if (!cmdbuf_obj->waited) { + *irq_status_ret = cmdbuf_obj->cmdbuf_id; + cmdbuf_obj->waited = 1; + return 1; + } + } + } + + return 0; +} + +#ifdef IRQ_SIMULATION +void get_random_bytes(void *buf, int nbytes); +#if 0 +void hantrovcmd_trigger_irq_0(struct timer_list* timer) +{ + PDEBUG("trigger core 0 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[0]); +} +void hantrovcmd_trigger_irq_1(struct timer_list* timer) +{ + PDEBUG("trigger core 1 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[1]); +} +#else +void hantrovcmd_trigger_irq(struct timer_list *timer) +{ + + u32 timer_id=0; + u32 core_id=0; + u32 i; + for(i=0;i<10000;i++) + { + if(timer_reserve[i].timer==timer) + { + timer_id=timer_reserve[i].timer_id; + core_id = timer_reserve[i].core_id; + break; + } + } + PDEBUG("trigger core 0 irq\n"); + hantrovcmd_isr(core_id,(void *)&hantrovcmd_data[core_id]); + del_timer(timer); + timer_reserve[timer_id].timer=NULL; +} + +#endif +#endif + +static unsigned int wait_cmdbuf_ready(struct file *filp,u16 cmdbuf_id,u32 *irq_status_ret) +{ + + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* new_cmdbuf_node=NULL; + struct hantrovcmd_dev* dev=NULL; + + if (cmdbuf_id != ANY_CMDBUF_ID) { + PDEBUG("wait_cmdbuf_ready\n"); + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; +#ifdef IRQ_SIMULATION + { + u64 random_num; + //get_random_bytes(&random_num, sizeof(u32)); + random_num = (u32)((u64)100*cmdbuf_obj->executing_time/(4096*2160)+50); + PDEBUG("random_num=%d\n",random_num); +#if 0 + /*init a timer to trigger irq*/ + if (cmdbuf_obj->core_id==0) + { + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + timer_setup(&timer0,hantrovcmd_trigger_irq_0,0); + timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer0); + } + + if (cmdbuf_obj->core_id==1) + { + //init_timer(&timer1); + //timer1.function = hantrovcmd_trigger_irq_1; + timer_setup(&timer1,hantrovcmd_trigger_irq_1,0); + timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer1); + } +#else + { + u32 i; + struct timer_list *temp_timer=NULL; + for(i=0;i<10000;i++) + { + if(timer_reserve[i].timer==NULL) + { + timer_reserve[i].timer_id=i; + timer_reserve[i].core_id=cmdbuf_obj->core_id; + temp_timer=timer_reserve[i].timer =&timer[i] ; + break; + } + } + //if (cmdbuf_obj->core_id==0) + { + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + timer_setup(temp_timer,hantrovcmd_trigger_irq,0); + temp_timer->expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(temp_timer); + } + } +#endif + } +#endif + + if(wait_event_interruptible(*dev->wait_queue, check_cmdbuf_irq(dev,cmdbuf_obj,irq_status_ret))) + { + PDEBUG("vcmd_wait_queue_0 interrupted\n"); + return -ERESTARTSYS; + } + + pr_debug("filp=%p, VCMD Wait CMDBUF [%d]\n", (void *)filp, cmdbuf_id); + return 0; + } else { + if (check_mc_cmdbuf_irq(filp, cmdbuf_obj, irq_status_ret)) + return 0; + if(wait_event_interruptible(mc_wait_queue, check_mc_cmdbuf_irq(filp,cmdbuf_obj,irq_status_ret))) + { + PDEBUG("multicore wait queue interrupted\n"); + return -ERESTARTSYS; + } + return 0; + } +} + + +long hantrovcmd_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + static int last_polling_cmd = 0; + struct process_manager_obj* process_manager_obj=NULL; + + if (cmd != HANTRO_VCMD_IOCH_POLLING_CMDBUF) { + last_polling_cmd = 0; + PDEBUG("ioctl cmd 0x%08x\n", cmd); + } else { + if (!last_polling_cmd) + PDEBUG("ioctl cmd 0x%08x\n", cmd); + last_polling_cmd = 1; + } + /* + * extract the type and number bitfields, and don't encode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if(_IOC_TYPE(cmd) != HANTRO_VCMD_IOC_MAGIC ) + return -ENOTTY; + if((_IOC_TYPE(cmd) == HANTRO_VCMD_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_VCMD_IOC_MAXNR) ) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); +#else + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#endif + if(err) + return -EFAULT; + + process_manager_obj = (struct process_manager_obj*)filp->private_data; + + switch (cmd) + { + case HANTRO_VCMD_IOCH_GET_CMDBUF_PARAMETER: + { + struct cmdbuf_mem_parameter local_cmdbuf_mem_data; + PDEBUG(" VCMD Reserve CMDBUF\n"); + local_cmdbuf_mem_data.cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.phy_status_cmdbuf_addr = vcmd_status_buf_mem_pool.busAddress; + local_cmdbuf_mem_data.phy_cmdbuf_addr = vcmd_buf_mem_pool.busAddress; + if (mmu_enable) { + local_cmdbuf_mem_data.mmu_phy_status_cmdbuf_addr = vcmd_status_buf_mem_pool.mmu_bus_address; + local_cmdbuf_mem_data.mmu_phy_cmdbuf_addr = vcmd_buf_mem_pool.mmu_bus_address; + } else { + local_cmdbuf_mem_data.mmu_phy_status_cmdbuf_addr = 0; + local_cmdbuf_mem_data.mmu_phy_cmdbuf_addr = 0; + } + local_cmdbuf_mem_data.base_ddr_addr = base_ddr_addr; + copy_to_user((struct cmdbuf_mem_parameter*)arg,&local_cmdbuf_mem_data,sizeof(struct cmdbuf_mem_parameter)); + break; + } + case HANTRO_VCMD_IOCH_GET_VCMD_PARAMETER: + { + struct config_parameter input_para; + PDEBUG(" VCMD get vcmd config parameter \n"); + copy_from_user(&input_para,(struct config_parameter*)arg,sizeof(struct config_parameter)); + if(vcmd_type_core_num[input_para.module_type]) + { + input_para.submodule_main_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr; + input_para.submodule_dec400_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_dec400_addr; + input_para.submodule_L2Cache_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr; + input_para.submodule_MMU_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_MMU_addr; + input_para.submodule_MMUWrite_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_MMUWrite_addr; + input_para.submodule_axife_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_axife_addr; + input_para.config_status_cmdbuf_id = vcmd_manager[input_para.module_type][0]->status_cmdbuf_id; + input_para.vcmd_hw_version_id = vcmd_manager[input_para.module_type][0]->hw_version_id; + input_para.vcmd_core_num = vcmd_type_core_num[input_para.module_type]; + } + else + { + input_para.submodule_main_addr = 0xffff; + input_para.submodule_dec400_addr = 0xffff; + input_para.submodule_L2Cache_addr = 0xffff; + input_para.submodule_MMU_addr = 0xffff; + input_para.submodule_MMUWrite_addr = 0xffff; + input_para.submodule_axife_addr = 0xffff; + input_para.config_status_cmdbuf_id = 0; + input_para.vcmd_core_num = 0; + input_para.vcmd_hw_version_id =HW_ID_1_0_C; + } + copy_to_user((struct config_parameter*)arg,&input_para,sizeof(struct config_parameter)); + break; + } + case HANTRO_VCMD_IOCH_RESERVE_CMDBUF: + { + int ret; + struct exchange_parameter input_para; + copy_from_user(&input_para,(struct exchange_parameter*)arg,sizeof(struct exchange_parameter)); + ret = reserve_cmdbuf(filp,&input_para); + if (ret == 0) + copy_to_user((struct exchange_parameter*)arg,&input_para,sizeof(struct exchange_parameter)); + pr_debug("filp=%p, VCMD Reserve CMDBUF [%d]\n", (void *)filp, input_para.cmdbuf_id); + return ret; + } + + case HANTRO_VCMD_IOCH_LINK_RUN_CMDBUF: + { + struct exchange_parameter input_para; + long retVal; + copy_from_user(&input_para,(struct exchange_parameter*)arg,sizeof(struct exchange_parameter)); + + PDEBUG("VCMD link and run cmdbuf\n"); + retVal = pm_runtime_resume_and_get(&hantrovcmd_data[0].pdev->dev); + if (retVal < 0) + return retVal; + if (process_manager_obj) + process_manager_obj->pm_count++; + retVal = link_and_run_cmdbuf(filp,&input_para); + copy_to_user((struct exchange_parameter*)arg,&input_para,sizeof(struct exchange_parameter)); + return retVal; + break; + } + + case HANTRO_VCMD_IOCH_WAIT_CMDBUF: + { + + u16 cmdbuf_id; + unsigned int tmp; + u32 irq_status_ret=0; + __get_user(cmdbuf_id, (u16*)arg); + /*high 16 bits are core id, low 16 bits are cmdbuf_id*/ + + PDEBUG("VCMD wait for CMDBUF finishing. \n"); + + //TODO + tmp = wait_cmdbuf_ready(filp,cmdbuf_id,&irq_status_ret); + cmdbuf_id=(u16)irq_status_ret; + if (tmp==0) + { + __put_user(cmdbuf_id, (u16 *)arg); + return tmp;//return core_id + } + else + { + __put_user(0, (u16 *)arg); + return -1; + } + + break; + } + case HANTRO_VCMD_IOCH_RELEASE_CMDBUF: + { + u16 cmdbuf_id; + __get_user(cmdbuf_id, (u16*)arg); + /*16 bits are cmdbuf_id*/ + + PDEBUG("VCMD release CMDBUF\n"); + + pm_runtime_mark_last_busy(&hantrovcmd_data[0].pdev->dev); + pm_runtime_put_autosuspend(&hantrovcmd_data[0].pdev->dev); + if (process_manager_obj) + process_manager_obj->pm_count--; + release_cmdbuf(filp,cmdbuf_id); + return 0; + break; + } + case HANTRO_VCMD_IOCH_POLLING_CMDBUF: + { + u16 core_id; + __get_user(core_id, (u16*)arg); + /*16 bits are cmdbuf_id*/ + if(core_id>=total_vcmd_core_num) + return -1; + hantrovcmd_isr(core_id,&hantrovcmd_data[core_id]); + return 0; + break; + } + default: + { + break; + } + } + return 0; +} +/**********************************************************************************************************\ +*process manager object management +\***********************************************************************************************************/ +static struct process_manager_obj* create_process_manager_obj(void) +{ + struct process_manager_obj* process_manager_obj=NULL; + process_manager_obj=vmalloc(sizeof(struct process_manager_obj)); + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","vmalloc for process_manager_obj fail!"); + return process_manager_obj; + } + memset(process_manager_obj,0,sizeof(struct process_manager_obj)); + return process_manager_obj; +} + +static void free_process_manager_obj( struct process_manager_obj* process_manager_obj) +{ + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","free_process_manager_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(process_manager_obj); + return; +} + +static bi_list_node* create_process_manager_node(void) +{ + bi_list_node* current_node=NULL; + struct process_manager_obj* process_manager_obj=NULL; + + process_manager_obj=create_process_manager_obj(); + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","create_process_manager_obj fail!"); + return NULL; + } + process_manager_obj->total_exe_time = 0; + process_manager_obj->pm_count = 0; + spin_lock_init(&process_manager_obj->spinlock); + init_waitqueue_head(&process_manager_obj->wait_queue); + current_node=bi_list_create_node(); + if(current_node==NULL) + { + PDEBUG ("%s\n","bi_list_create_node fail!"); + free_process_manager_obj(process_manager_obj); + return NULL; + } + current_node->data = (void*)process_manager_obj; + return current_node; +} +static void free_process_manager_node(bi_list_node* process_node) +{ + struct process_manager_obj* process_manager_obj=NULL; + if(process_node==NULL) + { + PDEBUG ("%s\n","free_process_manager_node NULL"); + return; + } + process_manager_obj = (struct process_manager_obj*)process_node->data; + //free struct process_manager_obj + free_process_manager_obj (process_manager_obj); + //free current process_manager_obj entity. + bi_list_free_node(process_node); + return; +} + +static long release_process_node_cleanup(bi_list* list) +{ + bi_list_node* new_process_node=NULL; + + while(1) + { + new_process_node=list->head; + if(new_process_node==NULL) + break; + //remove node from list + bi_list_remove_node(list,new_process_node); + //remove node from list + free_process_manager_node(new_process_node); + } + return 0; +} + +static void create_kernel_process_manager(void) +{ + bi_list_node* process_manager_node; + struct process_manager_obj* process_manager_obj=NULL; + process_manager_node = create_process_manager_node(); + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + process_manager_obj->filp = NULL; + bi_list_insert_node_tail(&global_process_manager,process_manager_node); +} + +/* Update the last JMP cmd in cmdbuf_ojb in order to jump to next_cmdbuf_obj. */ +static void cmdbuf_update_jmp_cmd(int hw_version_id, + struct cmdbuf_obj *cmdbuf_obj, + struct cmdbuf_obj *next_cmdbuf_obj, + int jmp_IE_1) { + u32 *jmp_addr; + u32 operation_code; + + if(!cmdbuf_obj) + return; + + if(cmdbuf_obj->has_end_cmdbuf==0) + { + //need to link, current cmdbuf link to next cmdbuf + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + if (!next_cmdbuf_obj) { + // If next cmdbuf is not available, set the RDY to 0. + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code & ~JMP_RDY_1); + } else { + if(hw_version_id > HW_ID_1_0_C ) + { + //set next cmdbuf id + *(jmp_addr-1) = next_cmdbuf_obj->cmdbuf_id; + } + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->mmu_cmdbuf_busAddress)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->mmu_cmdbuf_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr); + } + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code |JMP_RDY_1|jmp_IE_1|((next_cmdbuf_obj->cmdbuf_size+7)/8)); + } + +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + pr_info("vcmd link, last cmdbuf content\n"); + for(i=cmdbuf_obj->cmdbuf_size/4 -8;icmdbuf_size/4;i++) + { + pr_info("current linked cmdbuf data %d =0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } +#endif + } +} + + +/* delink given cmd buffer (cmdbuf_node) and remove it from list. + Also modify the last JMP of buf P to point to cmdbuf N. + Used when a process is terminated but there are pending cmd bufs in vmcd list. + E.g., + before: + + L->L->...->P->X->N-> ... ->L + ^ ^ ^ + head cmdbuf_node tail + + end: + + L->L->...->P->N-> ... ->L + ^ ^ + head tail + + Return: pointer to N or NULL if N doesn't exist. + */ +void vcmd_delink_rm_cmdbuf(struct hantrovcmd_dev *dev, bi_list_node* cmdbuf_node) +{ + bi_list *list = &dev->list_manager; + struct cmdbuf_obj* cmdbuf_obj = (struct cmdbuf_obj*)cmdbuf_node->data; + bi_list_node* prev = cmdbuf_node->previous; + bi_list_node* next = cmdbuf_node->next; + + PDEBUG("Delink and remove cmdbuf [%d] from vcmd list.\n", cmdbuf_obj->cmdbuf_id); +#ifdef HANTRO_VCMD_DRIVER_DEBUG + if (prev) { + PDEBUG("prev cmdbuf [%d].\n", ((struct cmdbuf_obj*)prev->data)->cmdbuf_id); + } else { + PDEBUG("NO prev cmdbuf.\n"); + } + if (next) { + PDEBUG("next cmdbuf [%d].\n", ((struct cmdbuf_obj*)next->data)->cmdbuf_id); + } else { + PDEBUG("NO next cmdbuf.\n"); + } +#endif + + bi_list_remove_node(list, cmdbuf_node); + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(cmdbuf_node); + + cmdbuf_update_jmp_cmd(dev->hw_version_id, prev ? prev->data : NULL, next ? next->data : NULL, + dev->duration_without_int > INT_MIN_SUM_OF_IMAGE_SIZE); +} + +int hantrovcmd_open(struct inode *inode, struct file *filp) +{ + int result = 0; + struct hantrovcmd_dev *dev = hantrovcmd_data; + bi_list_node* process_manager_node; + unsigned long flags; + struct process_manager_obj* process_manager_obj=NULL; + + filp->private_data = NULL; + process_manager_node = create_process_manager_node(); + if(process_manager_node== NULL) + return -1; + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + process_manager_obj->filp = filp; + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + bi_list_insert_node_tail(&global_process_manager,process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + filp->private_data = process_manager_node->data; + + PDEBUG("dev opened\n"); + PDEBUG("process node %p for filp opened %p\n", (void *)process_manager_node, (void *)filp); + return result; +} + +int hantrovcmd_release(struct inode *inode, struct file *filp) +{ + struct hantrovcmd_dev *dev = hantrovcmd_data; + u32 core_id = 0; + u32 release_cmdbuf_num=0; + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + bi_list_node* process_manager_node; + struct process_manager_obj* process_manager_obj=NULL; + int vcmd_aborted = 0; // vcmd is aborted in this function + struct cmdbuf_obj* restart_cmdbuf = NULL; + + unsigned long flags; + long retVal=0; + + PDEBUG("dev closed for process %p via hantrovcmd_release\n", (void *)filp); + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type])) + return -ERESTARTSYS; + + for (core_id = 0;core_id < total_vcmd_core_num; core_id++) + { + if((&dev[core_id])==NULL) + continue; + spin_lock_irqsave(dev[core_id].spinlock, flags); + new_cmdbuf_node=dev[core_id].list_manager.head; + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj*)new_cmdbuf_node->data; + PDEBUG("Process %p is releasing: checking cmdbuf %d of process %p.\n", + filp, cmdbuf_obj_temp->cmdbuf_id, cmdbuf_obj_temp->filp); + if (dev[core_id].hwregs && (cmdbuf_obj_temp->filp == filp)) + { + if(cmdbuf_obj_temp->cmdbuf_run_done) + { + cmdbuf_obj_temp->cmdbuf_need_remove=1; + retVal=release_cmdbuf_node(&dev[core_id].list_manager,new_cmdbuf_node); + if(retVal==1) + cmdbuf_obj_temp->process_manager_obj = NULL; + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==0) + { + cmdbuf_obj_temp->cmdbuf_data_linked = 1; + cmdbuf_obj_temp->cmdbuf_run_done=1; + cmdbuf_obj_temp->cmdbuf_need_remove=1; + retVal=release_cmdbuf_node(&dev[core_id].list_manager,new_cmdbuf_node); + if(retVal==1) + cmdbuf_obj_temp->process_manager_obj = NULL; + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==1 && dev[core_id].working_state==WORKING_STATE_IDLE) + { + vcmd_delink_rm_cmdbuf(&dev[core_id], new_cmdbuf_node); + if (restart_cmdbuf == cmdbuf_obj_temp) + restart_cmdbuf = new_cmdbuf_node->next ? new_cmdbuf_node->next->data : NULL; + if (restart_cmdbuf) { + PDEBUG("Set restart cmdbuf [%d].\n", restart_cmdbuf->cmdbuf_id); + } else { + PDEBUG("Set restart cmdbuf to NULL.\n"); + } + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==1 && dev[core_id].working_state==WORKING_STATE_WORKING) + { + bi_list_node* last_cmdbuf_node = NULL; + bi_list_node* done_cmdbuf_node = NULL; + int abort_cmdbuf_id; + int loop_count = 0; + + //abort the vcmd and wait + PDEBUG("Abort due to linked cmdbuf %d of current process.\n", cmdbuf_obj_temp->cmdbuf_id); + printk_vcmd_register_debug((const void *)dev->hwregs, "Before trigger to 0"); + // disable abort interrupt + //vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_IRQ_ABORT_EN,0); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER,0); + vcmd_aborted = 1; + + printk_vcmd_register_debug((const void *)dev->hwregs,"After trigger to 0"); + // Wait vcmd core aborted and vcmd enters IDLE mode. + //while (dev[core_id].working_state != WORKING_STATE_IDLE) { + while (vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_WORK_STATE)) { + loop_count++; + if (!(loop_count % 10)) { + u32 irq_status = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + pr_err("hantrovcmd: expected idle state, but irq status = 0x%0x\n", irq_status); + pr_err("hantrovcmd: vcmd current status is %d\n", vcmd_get_register_value((const void *)dev->hwregs, dev[core_id].reg_mirror, HWIF_VCMD_WORK_STATE)); + } + mdelay(10); // wait 10ms + if (loop_count > 100) { // too long + pr_err("hantrovcmd: too long before vcmd core to IDLE state\n"); + process_manager_obj = (struct process_manager_obj*)filp->private_data; + if (process_manager_obj) + { + while(process_manager_obj->pm_count > 0) + { + pm_runtime_mark_last_busy(&dev[0].pdev->dev); + pm_runtime_put_autosuspend(&dev[0].pdev->dev); + process_manager_obj->pm_count--; + } + } + spin_unlock_irqrestore(dev[core_id].spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type]); + return -ERESTARTSYS; + } + } + dev[core_id].working_state = WORKING_STATE_IDLE; + // clear interrupt & restore abort_e + if (vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_IRQ_ABORT)) { + PDEBUG("Abort interrupt triggered, now clear it to avoid abort int...\n"); + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, 0x1<<4); + PDEBUG("Now irq status = 0x%0x.\n", vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET)); + } + //printk_vcmd_register_debug((const void *)dev->hwregs, "vcmd status to IDLE"); + + abort_cmdbuf_id = vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + PDEBUG("Abort when executing cmd buf %d.\n", abort_cmdbuf_id); + dev[core_id].sw_cmdbuf_rdy_num = 0; + dev[core_id].duration_without_int = 0; + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT,0); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,0); + + /* Mark cmdbuf_run_done to 1 for all the cmd buf executed. */ + done_cmdbuf_node = dev[core_id].list_manager.head; + while (done_cmdbuf_node) { + if (!((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_run_done) { + ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_run_done = 1; + ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_data_linked = 0; + PDEBUG("Set cmdbuf [%d] cmdbuf_run_done to 1.\n", ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id); + } + if (((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id == abort_cmdbuf_id) + break; + done_cmdbuf_node = done_cmdbuf_node->next; + } + if (cmdbuf_obj_temp->cmdbuf_run_done) { + /* current cmdbuf is in fact has been executed, but due to interrupt is not triggered, the status is not updated. + Just delink and remove it from the list. */ + if (done_cmdbuf_node && done_cmdbuf_node->data) { + PDEBUG("done_cmdbuf_node is cmdbuf [%d].\n", ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id); + } + done_cmdbuf_node = done_cmdbuf_node->next; + if (done_cmdbuf_node) + restart_cmdbuf = (struct cmdbuf_obj*)done_cmdbuf_node->data; + if (restart_cmdbuf) { + PDEBUG("Set restart cmdbuf [%d] via if.\n", restart_cmdbuf->cmdbuf_id); + } + } else { + last_cmdbuf_node = new_cmdbuf_node; + /* cmd buf num from aborted cmd buf to current cmdbuf_obj_temp */ + if (cmdbuf_obj_temp->cmdbuf_id != abort_cmdbuf_id) { + last_cmdbuf_node = new_cmdbuf_node->previous; + + while (last_cmdbuf_node && + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_id != abort_cmdbuf_id) { + restart_cmdbuf = (struct cmdbuf_obj*)last_cmdbuf_node->data; + last_cmdbuf_node = last_cmdbuf_node->previous; + dev[core_id].sw_cmdbuf_rdy_num++; + dev[core_id].duration_without_int += restart_cmdbuf->executing_time; + PDEBUG("Keep valid cmdbuf [%d] in the list.\n", restart_cmdbuf->cmdbuf_id); + } + } + if (restart_cmdbuf) { + PDEBUG("Set restart cmdbuf [%d] via else.\n", restart_cmdbuf->cmdbuf_id); + } + } + + // remove first linked cmdbuf from list + vcmd_delink_rm_cmdbuf(&dev[core_id], new_cmdbuf_node); + + } + release_cmdbuf_num++; + PDEBUG("release reserved cmdbuf\n"); + } else if (vcmd_aborted && !cmdbuf_obj_temp->cmdbuf_run_done) { + /* VCMD is aborted, need to re-calculate the duration_without_int */ + if (!restart_cmdbuf) + restart_cmdbuf = cmdbuf_obj_temp; /* first cmdbuf to be restarted */ + dev[core_id].duration_without_int += cmdbuf_obj_temp->executing_time; + dev[core_id].sw_cmdbuf_rdy_num++; + } + + new_cmdbuf_node = new_cmdbuf_node->next; + } + + if (restart_cmdbuf) { + u32 irq_status1, irq_status2; + PDEBUG("Restart from cmdbuf [%d] after aborting.\n", restart_cmdbuf->cmdbuf_id); + + irq_status1 = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + vcmd_write_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, irq_status1); + irq_status2 = vcmd_read_reg((const void *)dev->hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + PDEBUG("Clear irq status from 0x%0x -> 0x%0x\n", irq_status1, irq_status2); + if (mmu_enable) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR, + (u32)(restart_cmdbuf->mmu_cmdbuf_busAddress)); + if(sizeof(size_t) == 8) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(restart_cmdbuf->mmu_cmdbuf_busAddress)>>32)); + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(restart_cmdbuf->cmdbuf_busAddress-base_ddr_addr)); + if(sizeof(size_t) == 8) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(restart_cmdbuf->cmdbuf_busAddress-base_ddr_addr)>>32)); + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT,0); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_LENGTH,(u32)((restart_cmdbuf->cmdbuf_size+7)/8)); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID,restart_cmdbuf->cmdbuf_id); + PDEBUG("====dev->sw_cmdbuf_rdy_num is %d\n", dev[core_id].sw_cmdbuf_rdy_num); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev[core_id].sw_cmdbuf_rdy_num); + printk_vcmd_register_debug((const void *)dev->hwregs, "before restart"); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER,1); + + PDEBUG("Restart from cmdbuf [%d] after aborting: start trigger = %d.\n", restart_cmdbuf->cmdbuf_id, + vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER)); + PDEBUG("dev state from %d -> WORKING.\n", dev[core_id].working_state); + dev[core_id].working_state = WORKING_STATE_WORKING; + printk_vcmd_register_debug((const void *)dev->hwregs, "after restart"); + } else { + PDEBUG("No more command buffer to be restarted!\n"); + } + spin_unlock_irqrestore(dev[core_id].spinlock, flags); + // VCMD aborted but not restarted, nedd to wake up + if (vcmd_aborted && !restart_cmdbuf) + wake_up_interruptible_all(dev[core_id].wait_queue); + } + + if(release_cmdbuf_num) + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while(1) + { + if(process_manager_node == NULL) + break; + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + if(process_manager_obj->filp == filp) + break; + process_manager_node = process_manager_node->next; + } + if (process_manager_obj) + { + while(process_manager_obj->pm_count > 0) + { + pm_runtime_mark_last_busy(&dev[0].pdev->dev); + pm_runtime_put_autosuspend(&dev[0].pdev->dev); + process_manager_obj->pm_count--; + } + } + //remove node from list + PDEBUG("process node %p for filp to be removed: %p\n", (void *)process_manager_node, (void *)process_manager_obj->filp); + bi_list_remove_node(&global_process_manager,process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + free_process_manager_node(process_manager_node); + up(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type]); + return 0; +} + + +/*------------------------------------------------------------------------------ + Function name : vcmd_pcie_init + Description : Initialize PCI Hw access + + Return type : int + ------------------------------------------------------------------------------*/ +static int vcmd_init(struct platform_device *pdev) +{ + int i =0; + struct kernel_addr_desc addr; + struct pci_dev *g_vcmd_dev = NULL; /* PCI device structure. */ + + vcmd_buf_mem_pool.virtualAddress = dma_alloc_coherent(&pdev->dev, CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE, + &vcmd_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + + pr_info("Base memory val 0x%llx\n", vcmd_buf_mem_pool.busAddress); + pr_info("Base memory len 0x%x\n", CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE); + + vcmd_buf_mem_pool.size =CMDBUF_POOL_TOTAL_SIZE; + pr_info("Init: vcmd_buf_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_buf_mem_pool.busAddress); + + if (vcmd_buf_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_buf_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_buf_mem_pool.virtualAddress); + if (mmu_enable) { + addr.bus_address = vcmd_buf_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_buf_mem_pool.size; + if(MMUKernelMemNodeMap(&addr) != MMU_STATUS_OK) { + return -1; + } + vcmd_buf_mem_pool.mmu_bus_address = addr.mmu_bus_address; + pr_info("Init: vcmd_buf_mem_pool.mmu_bus_address=0x%llx.\n",(long long unsigned int)vcmd_buf_mem_pool.mmu_bus_address); + } + + vcmd_status_buf_mem_pool.busAddress = (void *)vcmd_buf_mem_pool.busAddress+CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.virtualAddress = (void *)vcmd_buf_mem_pool.virtualAddress+CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.size =CMDBUF_POOL_TOTAL_SIZE; + pr_info("Init: vcmd_status_buf_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_status_buf_mem_pool.busAddress); + if (vcmd_status_buf_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_status_buf_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_status_buf_mem_pool.virtualAddress); + if (mmu_enable) { + addr.bus_address = vcmd_status_buf_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_status_buf_mem_pool.size; + if(MMUKernelMemNodeMap(&addr) != MMU_STATUS_OK) { + return -1; + } + vcmd_status_buf_mem_pool.mmu_bus_address = addr.mmu_bus_address; + pr_info("Init: vcmd_status_buf_mem_pool.mmu_bus_address=0x%llx.\n",(long long unsigned int)vcmd_status_buf_mem_pool.mmu_bus_address); + } + + vcmd_registers_mem_pool.busAddress = (void *)vcmd_buf_mem_pool.busAddress+CMDBUF_POOL_TOTAL_SIZE*2; + vcmd_registers_mem_pool.virtualAddress = (void *)vcmd_buf_mem_pool.virtualAddress+CMDBUF_POOL_TOTAL_SIZE*2; + vcmd_registers_mem_pool.size =CMDBUF_VCMD_REGISTER_TOTAL_SIZE; + pr_info("Init: vcmd_registers_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_registers_mem_pool.busAddress); + + if (vcmd_registers_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_registers_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_registers_mem_pool.virtualAddress); + if (mmu_enable) { + addr.bus_address = vcmd_registers_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_registers_mem_pool.size; + if(MMUKernelMemNodeMap(&addr) != MMU_STATUS_OK) { + return -1; + } + vcmd_registers_mem_pool.mmu_bus_address = addr.mmu_bus_address; + pr_info("Init: vcmd_registers_mem_pool.mmu_bus_address=0x%llx.\n",(long long unsigned int)vcmd_registers_mem_pool.mmu_bus_address); + } + + + return 0; + +out_pci_disable_device: + pci_disable_device(g_vcmd_dev); +out: + return -1; +} + +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + bi_list_node* next_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + struct cmdbuf_obj* next_cmdbuf_obj=NULL; + u32 * jmp_addr=NULL; + u32 operation_code; + new_cmdbuf_node = last_linked_cmdbuf_node; + //for the first cmdbuf. + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_data_linked==0) ) + { + dev->sw_cmdbuf_rdy_num ++; + cmdbuf_obj->cmdbuf_data_linked=1; + dev->duration_without_int = 0; + if(cmdbuf_obj->has_end_cmdbuf==0) + { + if(cmdbuf_obj->no_normal_int_cmdbuf==1) + { + dev->duration_without_int = cmdbuf_obj->executing_time; + //maybe nop is modified, so write back. + if(dev->duration_without_int>=INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + operation_code = *(jmp_addr-4); + operation_code = JMP_IE_1|operation_code; + *(jmp_addr-4) = operation_code; + dev->duration_without_int = 0; + } + + } + } + } + } + while(1) + { + if(new_cmdbuf_node==NULL) + break; + if(new_cmdbuf_node->next==NULL) + break; + next_cmdbuf_node = new_cmdbuf_node->next; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + next_cmdbuf_obj = (struct cmdbuf_obj*)next_cmdbuf_node->data; + if(cmdbuf_obj->has_end_cmdbuf==0 && !next_cmdbuf_obj->cmdbuf_run_done) + { + //need to link, current cmdbuf link to next cmdbuf + PDEBUG("Link cmdbuf %d to cmdbuf %d", cmdbuf_obj->cmdbuf_id, next_cmdbuf_obj->cmdbuf_id); + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + if(dev->hw_version_id > HW_ID_1_0_C ) + { + //set next cmdbuf id + *(jmp_addr-1) = next_cmdbuf_obj->cmdbuf_id; + } + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->mmu_cmdbuf_busAddress)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->mmu_cmdbuf_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr); + } + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code |JMP_RDY_1|((next_cmdbuf_obj->cmdbuf_size+7)/8)); + next_cmdbuf_obj->cmdbuf_data_linked = 1; + dev->sw_cmdbuf_rdy_num ++; + //modify nop code of next cmdbuf + if(next_cmdbuf_obj->has_end_cmdbuf==0) + { + if(next_cmdbuf_obj->no_normal_int_cmdbuf==1) + { + dev->duration_without_int +=next_cmdbuf_obj->executing_time; + + //maybe we see the modified nop before abort, so need to write back. + if(dev->duration_without_int>=INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = next_cmdbuf_obj->cmdbuf_virtualAddress + (next_cmdbuf_obj->cmdbuf_size/4); + operation_code = *(jmp_addr-4); + operation_code = JMP_IE_1|operation_code; + *(jmp_addr-4) = operation_code; + dev->duration_without_int = 0; + } + } + } + else + { + dev->duration_without_int = 0; + } +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + pr_info("vcmd link, last cmdbuf content\n"); + for(i=cmdbuf_obj->cmdbuf_size/4 -8;icmdbuf_size/4;i++) + { + pr_info("current linked cmdbuf data %d =0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } +#endif + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + return; +} + +/* delink all the cmd buffers from the cmdbuf in front oflast_linked_cmdbuf_node + to head of the list. All the cmd bufs marked as X will be delinked. + E.g., + X->X->...->X->L->L-> ... ->L + ^ ^ ^ + head last_linked_cmdbuf_node tail + */ +static void vcmd_delink_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + + new_cmdbuf_node = last_linked_cmdbuf_node; + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_data_linked) + { + cmdbuf_obj->cmdbuf_data_linked = 0; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + dev->sw_cmdbuf_rdy_num=0; +} + +static void vcmd_start(struct hantrovcmd_dev *dev,bi_list_node* first_linked_cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + + if(dev->working_state == WORKING_STATE_IDLE) + { + if((first_linked_cmdbuf_node!=NULL) && dev->sw_cmdbuf_rdy_num) + { + cmdbuf_obj = (struct cmdbuf_obj*)first_linked_cmdbuf_node->data; + printk_vcmd_register_debug((const void *)dev->hwregs, "vcmd_start enters"); + //0x40 + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_AXI_CLK_GATE_DISABLE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE,1);//this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_CORE_CLK_GATE_DISABLE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_ABORT_MODE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RESET_CORE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RESET_ALL,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_START_TRIGGER,0); + //0x48 + if(dev->hw_version_id <= HW_ID_1_0_C) + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_INTCMD_EN,0xffff); + else + { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_JMPP_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_JMPD_EN,1); + } + + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_RESET_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ABORT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_CMDERR_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_TIMEOUT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_BUSERR_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ENDCMD_EN,1); + //0x4c + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_TIMEOUT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_TIMEOUT_CYCLES,0x1dcd6500); + if (mmu_enable) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(cmdbuf_obj->mmu_cmdbuf_busAddress)); + if(sizeof(size_t) == 8) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(cmdbuf_obj->mmu_cmdbuf_busAddress)>>32)); + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)); + if(sizeof(size_t) == 8) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32)); + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXE_CMDBUF_LENGTH,(u32)((cmdbuf_obj->cmdbuf_size+7)/8)); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev->sw_cmdbuf_rdy_num); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MAX_BURST_LEN,0x10); + if(dev->hw_version_id > HW_ID_1_0_C ) + { + vcmd_write_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID,(u32)cmdbuf_obj->cmdbuf_id); + } + vcmd_write_reg((const void *)dev->hwregs,0x40,dev->reg_mirror[0x40/4]); + vcmd_write_reg((const void *)dev->hwregs,0x44,vcmd_read_reg((const void *)dev->hwregs,0x44)); + vcmd_write_reg((const void *)dev->hwregs,0x48,dev->reg_mirror[0x48/4]); + vcmd_write_reg((const void *)dev->hwregs,0x4c,dev->reg_mirror[0x4c/4]); + vcmd_write_reg((const void *)dev->hwregs,0x50,dev->reg_mirror[0x50/4]); + vcmd_write_reg((const void *)dev->hwregs,0x54,dev->reg_mirror[0x54/4]); + vcmd_write_reg((const void *)dev->hwregs,0x58,dev->reg_mirror[0x58/4]); + vcmd_write_reg((const void *)dev->hwregs,0x5c,dev->reg_mirror[0x5c/4]); + vcmd_write_reg((const void *)dev->hwregs,0x60,dev->reg_mirror[0x60/4]); + vcmd_write_reg((const void *)dev->hwregs,0x64,0xffffffff);//not interrupt cpu + + dev->working_state = WORKING_STATE_WORKING; + //start + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE,0);//this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_START_TRIGGER,1); + vcmd_write_reg((const void *)dev->hwregs,0x40,dev->reg_mirror[0x40/4]); + + printk_vcmd_register_debug((const void *)dev->hwregs, "vcmd_start exits "); + } + } + +} + +static void create_read_all_registers_cmdbuf(struct exchange_parameter* input_para) +{ + u32 register_range[]={VCMD_ENCODER_REGISTER_SIZE, + VCMD_IM_REGISTER_SIZE, + VCMD_DECODER_REGISTER_SIZE, + VCMD_JPEG_ENCODER_REGISTER_SIZE, + VCMD_JPEG_DECODER_REGISTER_SIZE}; + u32 counter_cmdbuf_size=0; + u32 * set_base_addr=vcmd_buf_mem_pool.virtualAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE/4; + //u32 *status_base_virt_addr=vcmd_status_buf_mem_pool.virtualAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE/4+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2/4+0); + ptr_t status_base_phy_addr=vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2+0); + u32 map_status_base_phy_addr=vcmd_status_buf_mem_pool.mmu_bus_address + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2+0); + u32 offset_inc=0; + if(vcmd_manager[input_para->module_type][0]->hw_version_id>HW_ID_1_0_C) + { + pr_info("vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n",vcmd_manager[input_para->module_type][0]->hw_version_id); + + //read vcmd executing cmdbuf id registers to ddr for balancing core load. + *(set_base_addr+0) = (OPCODE_RREG) |(1<<16) |(EXECUTING_CMDBUF_ID_ADDR*4); + counter_cmdbuf_size += 4; + *(set_base_addr+1) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr+2) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+3) = 0; + counter_cmdbuf_size += 4; + + //read main IP all registers + *(set_base_addr+4) = (OPCODE_RREG) |((register_range[input_para->module_type]/4)<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+5) = map_status_base_phy_addr; + } else { + *(set_base_addr+5) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+6) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+6) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+6) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+7) = 0; + counter_cmdbuf_size += 4; + if(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr != 0xffff) + { + offset_inc = 4; + status_base_phy_addr=vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr/2+0); + map_status_base_phy_addr=vcmd_status_buf_mem_pool.mmu_bus_address + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr/2+0); + //read L2cache IP first register + *(set_base_addr+8) = (OPCODE_RREG) |(1<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+9) = map_status_base_phy_addr; + } else { + *(set_base_addr+9) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+10) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+10) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+10) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+11) = 0; + counter_cmdbuf_size += 4; + } + +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //read vcmd registers to ddr + *(set_base_addr+8+offset_inc) = (OPCODE_RREG) |(27<<16) |(0); + counter_cmdbuf_size += 4; + *(set_base_addr+9+offset_inc) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr+10+offset_inc) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+11+offset_inc) = 0; + counter_cmdbuf_size += 4; + //JMP RDY = 0 + *(set_base_addr +12+offset_inc)= (OPCODE_JMP_RDY0) |0 |JMP_IE_1|0; + counter_cmdbuf_size += 4; + *(set_base_addr +13+offset_inc) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +14+offset_inc) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +15+offset_inc) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size=(16+offset_inc)*4; + } + else + { + pr_info("vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n",vcmd_manager[input_para->module_type][0]->hw_version_id); + //read all registers + *(set_base_addr+0) = (OPCODE_RREG) |((register_range[input_para->module_type]/4)<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+1) = map_status_base_phy_addr; + } else { + *(set_base_addr+1) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+2) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+2) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+2) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+3) = 0; + counter_cmdbuf_size += 4; +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //JMP RDY = 0 + *(set_base_addr +4)= (OPCODE_JMP_RDY0) |0 |JMP_IE_1|0; + counter_cmdbuf_size += 4; + *(set_base_addr +5) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +6) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +7) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size=8*4; + } +} +static void read_main_module_all_registers(u32 main_module_type) +{ + int ret; + struct exchange_parameter input_para; + u32 irq_status_ret=0; + u32 *status_base_virt_addr; + + input_para.executing_time=0; + input_para.priority=CMDBUF_PRIORITY_NORMAL; + input_para.module_type = main_module_type; + input_para.cmdbuf_size=0; + ret = reserve_cmdbuf(NULL,&input_para); + vcmd_manager[main_module_type][0]->status_cmdbuf_id = input_para.cmdbuf_id; + create_read_all_registers_cmdbuf(&input_para); + link_and_run_cmdbuf(NULL,&input_para); + //msleep(1000); + hantrovcmd_isr(input_para.core_id, &hantrovcmd_data[input_para.core_id]); + wait_cmdbuf_ready(NULL,input_para.cmdbuf_id,&irq_status_ret); + status_base_virt_addr=vcmd_status_buf_mem_pool.virtualAddress + input_para.cmdbuf_id*CMDBUF_MAX_SIZE/4+(vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr/2/4+0); + pr_info("vc8000_vcmd_driver: main module register 0:0x%x\n",*status_base_virt_addr); + pr_info("vc8000_vcmd_driver: main module register 50:0x%08x\n",*(status_base_virt_addr+50)); + pr_info("vc8000_vcmd_driver: main module register 54:0x%08x\n",*(status_base_virt_addr+54)); + pr_info("vc8000_vcmd_driver: main module register 56:0x%08x\n",*(status_base_virt_addr+56)); + pr_info("vc8000_vcmd_driver: main module register 309:0x%x\n",*(status_base_virt_addr+309)); + //don't release cmdbuf because it can be used repeatedly + //release_cmdbuf(input_para.cmdbuf_id); +} + +int hantrovcmd_init(struct platform_device *pdev) +{ + int i,k; + int result; + struct kernel_addr_desc addr; + + //total_vcmd_core_num = sizeof(vcmd_core_array)/sizeof(struct vcmd_config); + result = vcmd_init(pdev); + if(result) + goto err; + + for (i = 0; i< total_vcmd_core_num; i++) + { + pr_info("vcmd: module init - vcmdcore[%d] addr =0x%llx\n",i, + (long long unsigned int)vcmd_core_array[i].vcmd_base_addr); + } + hantrovcmd_data = (struct hantrovcmd_dev *)vmalloc(sizeof(struct hantrovcmd_dev)*total_vcmd_core_num); + if (hantrovcmd_data == NULL) + goto err; + memset(hantrovcmd_data,0,sizeof(struct hantrovcmd_dev)*total_vcmd_core_num); + hantrovcmd_data[0].pdev = pdev; + for(k=0;k\n", + hantrovcmd_major); + goto err1; + } + else if(result != 0) /* this is for dynamic major */ + { + hantrovcmd_major = result; + } +#endif + result = vcmd_reserve_IO(); + if(result < 0) + { + goto err; + } + vcmd_reset_asic(hantrovcmd_data); + + hantrovcmd_data[0].vcmd_core_cfg.vcmd_irq = platform_get_irq(pdev, 0); + pr_info("%s:get irq %d\n",__func__,hantrovcmd_data[0].vcmd_core_cfg.vcmd_irq); + + /* get the IRQ line */ + for (i=0;i busy, change your config. core_id=%d\n", + hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq,i); + vcmd_release_IO(); + goto err; + } else { + pr_info("vc8000_vcmd_driver: request IRQ <%d> successfully for subsystem %d\n", + hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq, i); + } + } + else + { + pr_info("vc8000_vcmd_driver: IRQ not in use!\n"); + } + } + //cmdbuf pool allocation + //init_vcmd_non_cachable_memory_allocate(); + //for cmdbuf management + cmdbuf_used_pos=0; + for(k=0;kdev, CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE, + vcmd_buf_mem_pool.virtualAddress, vcmd_buf_mem_pool.busAddress); + } + if (vcmd_status_buf_mem_pool.virtualAddress) { + if (mmu_enable) { + addr.bus_address = vcmd_status_buf_mem_pool.busAddress; + addr.size = vcmd_status_buf_mem_pool.size; + MMUKernelMemNodeUnmap(&addr); + } + } + if (vcmd_registers_mem_pool.virtualAddress) { + if (mmu_enable) { + addr.bus_address = vcmd_registers_mem_pool.busAddress; + addr.size = vcmd_registers_mem_pool.size; + MMUKernelMemNodeUnmap(&addr); + } + } + if (hantrovcmd_data != NULL) + vfree(hantrovcmd_data); + pr_info("vc8000_vcmd_driver: module not inserted\n"); + return result; +} + +void hantrovcmd_cleanup(struct platform_device *pdev) +{ + int i=0; + u32 result; + + for(i=0;idev, CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE, + vcmd_buf_mem_pool.virtualAddress, vcmd_buf_mem_pool.busAddress); + +#ifndef DYNAMIC_MALLOC_VCMDNODE + if (g_cmdbuf_obj_pool) { + vfree(g_cmdbuf_obj_pool); + g_cmdbuf_obj_pool = NULL; + } + if (g_cmdbuf_node_pool) { + vfree(g_cmdbuf_node_pool); + g_cmdbuf_node_pool = NULL; + } +#endif + pr_info("vc8000_vcmd_driver: module removed\n"); + return; +} + +static int vcmd_reserve_IO(void) +{ + u32 hwid; + int i; + u32 found_hw = 0; + + for (i=0;i> 16) & 0xFFFF) != VCMD_HW_ID ) + { + pr_info("hantrovcmd: HW not found at 0x%llx\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr); + iounmap(( void *) hantrovcmd_data[i].hwregs); + release_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + hantrovcmd_data[i].hwregs = NULL; + continue; + } + + found_hw = 1; + + pr_info( + "hantrovcmd: HW at base <0x%llx> with ID <0x%08x>\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hwid); + + } + + if (found_hw == 0) + { + pr_err("hantrovcmd: NO ANY HW found!!\n"); + return -1; + } + + return 0; +} + +static void vcmd_release_IO(void) +{ + u32 i; + for (i=0;ispinlock, flags); + if (dev->list_manager.head==NULL) + { + u32 irq_status1=0; + if (count % 100000 == 0) + PDEBUG( "hantrovcmd_isr:received IRQ but core has nothing to do.\n"); + irq_status = vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,irq_status); + spin_unlock_irqrestore(dev->spinlock, flags); + irq_status1 = vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); + if (count++ % 100000 == 0) + PDEBUG( "hantrovcmd_isr: irq status 0x%x -> 0x%x\n", irq_status, irq_status1); + return IRQ_HANDLED; + } + + PDEBUG( "hantrovcmd_isr:received IRQ!\n"); + irq_status = vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + for(i=0;ihwregs, i*4); + pr_info("vcmd register %d:0x%x\n",i,fordebug); + } + } +#endif + + if(!irq_status) + { + //pr_info("hantrovcmd_isr error,irq_status :0x%x",irq_status); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + + PDEBUG( "irq_status of %d is:%x\n",dev->core_id,irq_status); + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,irq_status); + dev->reg_mirror[VCMD_REGISTER_INT_STATUS_OFFSET/4] = irq_status; + + if((dev->hw_version_id > HW_ID_1_0_C )&&(irq_status&0x3f)) + { + //if error,read from register directly. + cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else if((dev->hw_version_id > HW_ID_1_0_C )) + { + //read cmdbuf id from ddr +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + pr_info("ddr vcmd register phy_addr=0x%x\n",dev->vcmd_reg_mem_busAddress); + pr_info("ddr vcmd register virt_addr=0x%x\n",dev->vcmd_reg_mem_virtualAddress); + for(i=0;ivcmd_reg_mem_virtualAddress+i); + pr_info("ddr vcmd register %d:0x%x\n",i,fordebug); + } + } +#endif + + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + + + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_RESET)) + { + //reset error,all cmdbuf that is not done will be run again. + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + //find the first run_done=0 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + base_cmdbuf_node = new_cmdbuf_node; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + handled++; + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ABORT)) + { + //abort error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmdbuf that triggers ABORT + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + //to let high priority cmdbuf be inserted + wake_up_interruptible_all(dev->wait_abort_queue); + wake_up_interruptible_all(&mc_wait_queue); + handled++; + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_BUSERR)) + { + //bus error, don't need to reset where to record status? + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the buserr cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_BUSERR; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + wake_up_interruptible_all(&mc_wait_queue); + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_TIMEOUT)) + { + //time out,need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the timeout cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //reset + vcmd_reset_current_asic(dev); + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + wake_up_interruptible_all(&mc_wait_queue); + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_CMDERR)) + { + //command error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmderror cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_CMDERR;//cmderr + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + wake_up_interruptible_all(&mc_wait_queue); + return IRQ_HANDLED; + } + + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ENDCMD)) + { + //end command interrupt + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + //find the end cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->has_end_cmdbuf == 1)&&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + wake_up_interruptible_all(&mc_wait_queue); + return IRQ_HANDLED; + } + if(dev->hw_version_id <= HW_ID_1_0_C ) + cmdbuf_id = vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_INTCMD); + if(cmdbuf_id) + { + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + // interrupt cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + handled++; + } + + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + if(!handled) + { + PDEBUG("IRQ received, but not hantro's!\n"); + } + wake_up_interruptible_all(&mc_wait_queue); + return IRQ_HANDLED; +} + +void vcmd_reset_asic(struct hantrovcmd_dev * dev) +{ + int i,n; + u32 result; + for (n=0;nhwregs!=NULL) + { + //disable interrupt at first + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_CTL_OFFSET,0x0000); + //reset all + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_CONTROL_OFFSET,0x0002); + //read status register + result =vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); + //clean status register + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,result); + } + +} + +static void printk_vcmd_register_debug(const void *hwregs, char * info) { +#ifdef HANTRO_VCMD_DRIVER_DEBUG + u32 i, fordebug; + for(i = 0; i < ASIC_VCMD_SWREG_AMOUNT; i++) { + fordebug = vcmd_read_reg ((const void *)hwregs, i*4); + pr_info("%s vcmd register %d:0x%x\n",info,i,fordebug); + } +#endif +} + +void hantrovcmd_reset(void) +{ + if (hantrovcmd_data) { + int i; + for(i=0;i= vcmd_buf_mem_pool.busAddress && (addr - vcmd_buf_mem_pool.busAddress + size) <= CMDBUF_POOL_TOTAL_SIZE) || + (addr >= vcmd_status_buf_mem_pool.busAddress && (addr - vcmd_status_buf_mem_pool.busAddress + size) <= CMDBUF_POOL_TOTAL_SIZE); + + return bInRange; +} \ No newline at end of file diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantroaxife.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantroaxife.h new file mode 100644 index 000000000..ae180f62f --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantroaxife.h @@ -0,0 +1,63 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef __HANTRO_AXIFE_H__ +#define __HANTRO_AXIFE_H__ + +#include +#include + +void AXIFEEnable (volatile unsigned char *hwregs); + +#endif diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrodec.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrodec.h new file mode 100644 index 000000000..66e31c5d7 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrodec.h @@ -0,0 +1,199 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _HANTRODEC_H_ +#define _HANTRODEC_H_ +#ifdef __FREERTOS__ +#include "basetype.h" +#include "dev_common_freertos.h" +#elif defined(__linux__) +#include +#include +#include "hantrommu.h" +#else //For other os +//TODO... +#endif +#include "hantrovcmd.h" + +enum CoreType { + /* Decoder */ + HW_VC8000D = 0, + HW_VC8000DJ, + HW_BIGOCEAN, + HW_VCMD, + HW_MMU, //if set HW_MMU_WR, then HW_MMU means HW_MMU_RD + HW_MMU_WR, + HW_DEC400, + HW_L2CACHE, + HW_SHAPER, + /* Encoder*/ + /* Auxiliary IPs */ + HW_NOC, + HW_AXIFE, + HW_APBFILTER, + HW_CORE_MAX /* max number of cores supported */ +}; + +struct core_desc { + __u32 id; /* id of the subsystem */ + __u32 type; /* type of core to be written */ + __u32 *regs; /* pointer to user registers */ + __u32 size; /* size of register space */ + __u32 reg_id; /* id of reigster to be read/written */ +}; + +struct regsize_desc { + __u32 slice; /* id of the slice */ + __u32 id; /* id of the subsystem */ + __u32 type; /* type of core to be written */ + __u32 size; /* iosize of the core */ +}; + +struct core_param { + __u32 slice; /* id of the slice */ + __u32 id; /* id of the subsystem */ + __u32 type; /* type of core to be written */ + __u32 size; /* iosize of the core */ + __u32 asic_id; /* asic id of the core */ +}; + + +struct subsys_desc { + __u32 subsys_num; /* total subsystems count */ + __u32 subsys_vcmd_num; /* subsystems with vcmd */ +}; + +struct axife_cfg { + __u8 axi_rd_chn_num; + __u8 axi_wr_chn_num; + __u8 axi_rd_burst_length; + __u8 axi_wr_burst_length; + __u8 fe_mode; + __u32 id; +}; + +struct apbfilter_cfg { + __u32 nbr_mask_regs; + __u32 mask_reg_offset; + __u32 page_sel_addr; + __u8 num_mode; + __u8 mask_bits_per_reg; + __u32 id; /* id of the subsystem */ + __u32 type; /* type of core to be written */ + __u32 has_apbfilter; +}; +/* Use 'k' as magic number */ +#define HANTRODEC_IOC_MAGIC 'k' + +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ + +#define HANTRODEC_PP_INSTANCE _IO(HANTRODEC_IOC_MAGIC, 1) +#define HANTRODEC_HW_PERFORMANCE _IO(HANTRODEC_IOC_MAGIC, 2) +#define HANTRODEC_IOCGHWOFFSET _IOR(HANTRODEC_IOC_MAGIC, 3, unsigned long *) +#define HANTRODEC_IOCGHWIOSIZE _IOR(HANTRODEC_IOC_MAGIC, 4, struct regsize_desc *) + +#define HANTRODEC_IOC_CLI _IO(HANTRODEC_IOC_MAGIC, 5) +#define HANTRODEC_IOC_STI _IO(HANTRODEC_IOC_MAGIC, 6) +#define HANTRODEC_IOC_MC_OFFSETS _IOR(HANTRODEC_IOC_MAGIC, 7, unsigned long *) +#define HANTRODEC_IOC_MC_CORES _IOR(HANTRODEC_IOC_MAGIC, 8, unsigned int *) + + +#define HANTRODEC_IOCS_DEC_PUSH_REG _IOW(HANTRODEC_IOC_MAGIC, 9, struct core_desc *) +#define HANTRODEC_IOCS_PP_PUSH_REG _IOW(HANTRODEC_IOC_MAGIC, 10, struct core_desc *) + +#define HANTRODEC_IOCH_DEC_RESERVE _IO(HANTRODEC_IOC_MAGIC, 11) +#define HANTRODEC_IOCT_DEC_RELEASE _IO(HANTRODEC_IOC_MAGIC, 12) +#define HANTRODEC_IOCQ_PP_RESERVE _IO(HANTRODEC_IOC_MAGIC, 13) +#define HANTRODEC_IOCT_PP_RELEASE _IO(HANTRODEC_IOC_MAGIC, 14) + +#define HANTRODEC_IOCX_DEC_WAIT _IOWR(HANTRODEC_IOC_MAGIC, 15, struct core_desc *) +#define HANTRODEC_IOCX_PP_WAIT _IOWR(HANTRODEC_IOC_MAGIC, 16, struct core_desc *) + +#define HANTRODEC_IOCS_DEC_PULL_REG _IOWR(HANTRODEC_IOC_MAGIC, 17, struct core_desc *) +#define HANTRODEC_IOCS_PP_PULL_REG _IOWR(HANTRODEC_IOC_MAGIC, 18, struct core_desc *) + +#define HANTRODEC_IOCG_CORE_WAIT _IOR(HANTRODEC_IOC_MAGIC, 19, int *) + +#define HANTRODEC_IOX_ASIC_ID _IOWR(HANTRODEC_IOC_MAGIC, 20, struct core_param *) + +#define HANTRODEC_IOCG_CORE_ID _IOR(HANTRODEC_IOC_MAGIC, 21, unsigned long) + +#define HANTRODEC_IOCS_DEC_WRITE_REG _IOW(HANTRODEC_IOC_MAGIC, 22, struct core_desc *) + +#define HANTRODEC_IOCS_DEC_READ_REG _IOWR(HANTRODEC_IOC_MAGIC, 23, struct core_desc *) + +#define HANTRODEC_IOX_ASIC_BUILD_ID _IOWR(HANTRODEC_IOC_MAGIC, 24, __u32 *) + +#define HANTRODEC_IOX_SUBSYS _IOWR(HANTRODEC_IOC_MAGIC, 25, struct subsys_desc *) + +#define HANTRODEC_IOCX_POLL _IO(HANTRODEC_IOC_MAGIC, 26) + +#define HANTRODEC_DEBUG_STATUS _IO(HANTRODEC_IOC_MAGIC, 29) + +#define HANTRODEC_IOCS_DEC_WRITE_APBFILTER_REG _IOW(HANTRODEC_IOC_MAGIC, 30, struct core_desc *) + +#define HANTRODEC_IOC_APBFILTER_CONFIG _IOR(HANTRODEC_IOC_MAGIC, 31, struct apbfilter_cfg *) + +#define HANTRODEC_IOC_AXIFE_CONFIG _IOR(HANTRODEC_IOC_MAGIC, 32, struct axife_cfg *) + +#define HANTRODEC_IOC_MAXNR 32 + +#endif /* !_HANTRODEC_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrommu.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrommu.h new file mode 100644 index 000000000..9cd734214 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrommu.h @@ -0,0 +1,109 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _HANTROMMU_H_ +#define _HANTROMMU_H_ +#ifdef __FREERTOS__ +#include "basetype.h" +#include "dev_common_freertos.h" +#elif defined(__linux__) +#include +#endif + +#define REGION_IN_START 0x0 +#define REGION_IN_END 0x0 +#define REGION_OUT_START 0x0 +#define REGION_OUT_END 0x0 +#define REGION_PRIVATE_START 0x0 +#define REGION_PRIVATE_END 0x0 + +#define REGION_IN_MMU_START 0x1000 +#define REGION_IN_MMU_END 0x1000 +#define REGION_OUT_MMU_START 0x1000 +#define REGION_OUT_MMU_END 0x1000 +#define REGION_PRIVATE_MMU_START 0x1000 +#define REGION_PRIVATE_MMU_END 0x1000 + +enum MMUStatus { + MMU_STATUS_OK = 0, + + MMU_STATUS_FALSE = -1, + MMU_STATUS_INVALID_ARGUMENT = -2, + MMU_STATUS_INVALID_OBJECT = -3, + MMU_STATUS_OUT_OF_MEMORY = -4, + MMU_STATUS_NOT_FOUND = -19, +}; + +struct addr_desc { + void *virtual_address; /* buffer virtual address */ + unsigned int bus_address; /* buffer physical address */ + unsigned int size; /* physical size */ +}; + +struct kernel_addr_desc { + unsigned long long bus_address; /* buffer virtual address */ + unsigned long long mmu_bus_address; /* buffer physical address in MMU*/ + unsigned int size; /* physical size */ +}; + + +#define HANTRO_IOC_MMU 'm' + +#define HANTRO_IOCS_MMU_MEM_MAP _IOWR(HANTRO_IOC_MMU, 1, struct addr_desc *) +#define HANTRO_IOCS_MMU_MEM_UNMAP _IOWR(HANTRO_IOC_MMU, 2, struct addr_desc *) +#define HANTRO_IOCS_MMU_FLUSH _IOWR(HANTRO_IOC_MMU, 3, unsigned int *) +#define HANTRO_IOC_MMU_MAXNR 3 + +#endif + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrovcmd.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrovcmd.h new file mode 100644 index 000000000..d431dbb6d --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/hantrovcmd.h @@ -0,0 +1,192 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _VC8000_VCMD_DRIVER_H_ +#define _VC8000_VCMD_DRIVER_H_ +#ifdef __FREERTOS__ +#include "basetype.h" +#include "dev_common_freertos.h" +#elif defined(__linux__) +#include /* needed for the _IOW etc stuff used later */ +#endif + +#ifdef __FREERTOS__ +//addr_t has been defined in basetype.h //Now the FreeRTOS mem need to support 64bit env +#elif defined(__linux__) +#undef addr_t +#define addr_t ADDR_T_VCMD +typedef size_t addr_t; +#endif + +/* Use 'v' as magic number for vcmd */ +#define HANTRO_VCMD_IOC_MAGIC 'v' +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ + +#define HANTRO_VCMD_IOCH_GET_CMDBUF_PARAMETER _IOWR(HANTRO_VCMD_IOC_MAGIC, 20,struct cmdbuf_mem_parameter *) +#define HANTRO_VCMD_IOCH_GET_CMDBUF_POOL_SIZE _IOWR(HANTRO_VCMD_IOC_MAGIC, 21,unsigned long) +#define HANTRO_VCMD_IOCH_SET_CMDBUF_POOL_BASE _IOWR(HANTRO_VCMD_IOC_MAGIC, 22,unsigned long) + +#define HANTRO_VCMD_IOCH_GET_VCMD_PARAMETER _IOWR(HANTRO_VCMD_IOC_MAGIC, 24, struct config_parameter *) + +#define HANTRO_VCMD_IOCH_RESERVE_CMDBUF _IOWR(HANTRO_VCMD_IOC_MAGIC, 25,struct exchange_parameter *) +#define HANTRO_VCMD_IOCH_LINK_RUN_CMDBUF _IOR(HANTRO_VCMD_IOC_MAGIC, 26,u16 *) +#define HANTRO_VCMD_IOCH_WAIT_CMDBUF _IOR(HANTRO_VCMD_IOC_MAGIC, 27,u16 *) +#define HANTRO_VCMD_IOCH_RELEASE_CMDBUF _IOR(HANTRO_VCMD_IOC_MAGIC, 28,u16 *) + +#define HANTRO_VCMD_IOCH_POLLING_CMDBUF _IOR(HANTRO_VCMD_IOC_MAGIC, 40,u16 *) + +#define HANTRO_VCMD_IOC_MAXNR 50 + +/*priority support*/ + +#define MAX_CMDBUF_PRIORITY_TYPE 2 //0:normal priority,1:high priority + +#define CMDBUF_PRIORITY_NORMAL 0 +#define CMDBUF_PRIORITY_HIGH 1 + +#if 0 +#define OPCODE_WREG (0x01<<27) +#define OPCODE_END (0x02<<27) +#define OPCODE_NOP (0x03<<27) +#define OPCODE_RREG (0x16<<27) +#define OPCODE_INT (0x18<<27) +#define OPCODE_JMP (0x19<<27) +#define OPCODE_STALL (0x09<<27) +#define OPCODE_CLRINT (0x1a<<27) +#define OPCODE_JMP_RDY0 (0x19<<27) +#define OPCODE_JMP_RDY1 ((0x19<<27)|(1<<26)) +#define JMP_IE_1 (1<<25) +#define JMP_RDY_1 (1<<26) + +#define CLRINT_OPTYPE_READ_WRITE_1_CLEAR 0 +#define CLRINT_OPTYPE_READ_WRITE_0_CLEAR 1 +#define CLRINT_OPTYPE_READ_CLEAR 2 + +#define VC8000E_FRAME_RDY_INT_MASK 0x0001 +#define VC8000E_CUTREE_RDY_INT_MASK 0x0002 +#define VC8000E_DEC400_INT_MASK 0x0004 +#define VC8000E_L2CACHE_INT_MASK 0x0008 +#define VC8000E_MMU_INT_MASK 0x0010 + +#define VC8000D_FRAME_RDY_INT_MASK 0x0100 +#define VC8000D_DEC400_INT_MASK 0x0400 +#define VC8000D_L2CACHE_INT_MASK 0x0800 +#define VC8000D_MMU_INT_MASK 0x1000 +#endif + +#define HW_ID_1_0_C 0x43421001 +#define HW_ID_1_1_2 0x43421102 + +#define ANY_CMDBUF_ID 0xFFFF + +/*module_type support*/ + +enum vcmd_module_type{ + VCMD_TYPE_ENCODER = 0, + VCMD_TYPE_CUTREE, + VCMD_TYPE_DECODER, + VCMD_TYPE_JPEG_ENCODER, + VCMD_TYPE_JPEG_DECODER, + MAX_VCMD_TYPE +}; + +struct cmdbuf_mem_parameter +{ + u32 *virt_cmdbuf_addr; + addr_t phy_cmdbuf_addr; //cmdbuf pool base physical address + u32 mmu_phy_cmdbuf_addr; //cmdbuf pool base mmu mapping address + u32 cmdbuf_total_size; //cmdbuf pool total size in bytes. + u16 cmdbuf_unit_size; //one cmdbuf size in bytes. all cmdbuf have same size. + u32 *virt_status_cmdbuf_addr; + addr_t phy_status_cmdbuf_addr; //status cmdbuf pool base physical address + u32 mmu_phy_status_cmdbuf_addr; //status cmdbuf pool base mmu mapping address + u32 status_cmdbuf_total_size; //status cmdbuf pool total size in bytes. + u16 status_cmdbuf_unit_size; //one status cmdbuf size in bytes. all status cmdbuf have same size. + addr_t base_ddr_addr; //for pcie interface, hw can only access phy_cmdbuf_addr-pcie_base_ddr_addr. + //for other interface, this value should be 0? +}; + +struct config_parameter +{ + u16 module_type; //input vc8000e=0,cutree=1,vc8000d=2,jpege=3, jpegd=4 + u16 vcmd_core_num; //output, how many vcmd cores are there with corresponding module_type. + u16 submodule_main_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_dec400_addr; //output ,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_L2Cache_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_MMU_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_MMUWrite_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_axife_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 config_status_cmdbuf_id; // output , this status comdbuf save the all register values read in driver init.//used for analyse configuration in cwl. + u32 vcmd_hw_version_id; +}; + +/*need to consider how many memory should be allocated for status.*/ +struct exchange_parameter +{ + u32 executing_time; //input ;executing_time=encoded_image_size*(rdoLevel+1)*(rdoq+1); + u16 module_type; //input input vc8000e=0,IM=1,vc8000d=2, jpege=3, jpegd=4 + u16 cmdbuf_size; //input, reserve is not used; link and run is input. + u16 priority; //input,normal=0, high/live=1 + u16 cmdbuf_id; //output, it is unique in driver. + u16 core_id; //just used for polling. +}; + +#endif /* !_VC8000_VCMD_DRIVER_H_ */ diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.c new file mode 100644 index 000000000..e4e47c8ba --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.c @@ -0,0 +1,1286 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 Vivante Corporation +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kernel_allocator.h" + + +#define DISCRETE_PAGES 0 +//#define ALLOCATOR_DEBUG + +#define IS_ERROR(status) (status > 0) + +/******************************************************************************* +** +** ONERROR +** +** Jump to the error handler in case there is an error. +** +** ASSUMPTIONS: +** +** 'status' variable of int type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _ONERROR(prefix, func) \ + do \ + { \ + status = func; \ + if (IS_ERROR(status)) \ + { \ + goto OnError; \ + } \ + } \ + while (false) + +#define ONERROR(func) _ONERROR(, func) + +/******************************************************************************* +** +** ERR_BREAK +** +** Executes a break statement on error. +** +** ASSUMPTIONS: +** +** 'status' variable of int type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _ERR_BREAK(prefix, func){ \ + status = func; \ + if (IS_ERROR(status)) \ + { \ + break; \ + } \ + do { } while (false); \ + } + +#define ERR_BREAK(func) _ERR_BREAK(, func) + +/******************************************************************************* +** +** VERIFY_ARGUMENT +** +** Assert if an argument does not apply to the specified expression. If +** the argument evaluates to false, EINVAL will be +** returned from the current function. In retail mode this macro does +** nothing. +** +** ARGUMENTS: +** +** arg Argument to evaluate. +*/ +#define _VERIFY_ARGUMENT(prefix, arg) \ + do \ + { \ + if (!(arg)) \ + { \ + return EINVAL; \ + } \ + } \ + while (false) +#define VERIFY_ARGUMENT(arg) _VERIFY_ARGUMENT(, arg) + + +#define VM_FLAGS (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0) +#define current_mm_mmap_sem current->mm->mmap_lock +#else +#define current_mm_mmap_sem current->mm->mmap_sem +#endif + +#define GetPageCount(size, offset) ((((size) + ((offset) & ~PAGE_MASK)) + PAGE_SIZE - 1) >> PAGE_SHIFT) + +#ifndef ALLOCATOR_DEBUG +#define DEBUG_PRINT(...) \ + do { \ + } while (0) +#else +#undef DEBUG_PRINT +#define DEBUG_PRINT(...) printk(__VA_ARGS__) +#endif + + +struct mem_block +{ + int contiguous; + size_t size; + size_t numPages; + struct dma_buf *dmabuf; + struct dma_buf_attachment * attachment; + struct sg_table * sgt; + unsigned long * pagearray; + struct vm_area_struct * vma; + + union + { + /* Pointer to a array of pages. */ + struct + { + struct page *contiguousPages; + dma_addr_t dma_addr; + int exact; + }; + + struct + { + /* Pointer to a array of pointers to page. */ + struct page **nonContiguousPages; + + struct page **Pages1M; + int numPages1M; + int *isExact; + }; + }; +}; + +struct mem_node +{ + struct mem_block memBlk; + unsigned long busAddr; + int isImported; + struct list_head link; +}; + +struct file_node +{ + struct list_head memList; + struct file *filp; + struct list_head link; +}; + +static struct list_head fileList; + +static struct device *gdev = NULL; + +static DEFINE_SPINLOCK(mem_lock); + +static struct file_node * find_and_delete_file_node(struct file *filp) +{ + struct file_node *node; + struct file_node *temp; + + spin_lock(&mem_lock); + list_for_each_entry_safe(node, temp, &fileList, link) + { + if (node->filp == filp) + { + list_del(&node->link); + spin_unlock(&mem_lock); + return node; + } + } + spin_unlock(&mem_lock); + + return NULL; +} + +static struct file_node * get_file_node(struct file *filp) +{ + struct file_node *node; + + spin_lock(&mem_lock); + list_for_each_entry(node, &fileList, link) + { + if (node->filp == filp) + { + spin_unlock(&mem_lock); + return node; + } + } + spin_unlock(&mem_lock); + + return NULL; +} + +static struct mem_node * get_mem_node(struct file *filp, unsigned long bus_address, int imported) +{ + struct file_node *fnode; + struct mem_node *node; + + fnode = get_file_node(filp); + if (NULL == fnode) + { + return NULL; + } + + spin_lock(&mem_lock); + list_for_each_entry(node, &fnode->memList, link) + { + if (node->busAddr == bus_address && node->isImported == imported) + { + spin_unlock(&mem_lock); + return node; + } + } + spin_unlock(&mem_lock); + + return NULL; +} + +static int +AllocateMemory( + IN size_t Bytes, + OUT void * * Memory + ) +{ + void * memory = NULL; + int status = 0; + + /* Verify the arguments. */ + VERIFY_ARGUMENT(Bytes > 0); + VERIFY_ARGUMENT(Memory != NULL); + + if (Bytes > PAGE_SIZE) + { + memory = (void *) vmalloc(Bytes); + } + else + { + memory = (void *) kmalloc(Bytes, GFP_KERNEL | __GFP_NOWARN); + } + + if (memory == NULL) + { + /* Out of memory. */ + ONERROR(ENOMEM); + } + + /* Return pointer to the memory allocation. */ + *Memory = memory; + +OnError: + /* Return the status. */ + return status; +} + +int +static FreeMemory( + IN void * Memory + ) +{ + /* Verify the arguments. */ + VERIFY_ARGUMENT(Memory != NULL); + + /* Free the memory from the OS pool. */ + if (is_vmalloc_addr(Memory)) + { + vfree(Memory); + } + else + { + kfree(Memory); + } + + /* Success. */ + return 0; +} + + +static int +GetSGT( + IN struct mem_block *MemBlk, + IN size_t Offset, + IN size_t Bytes, + OUT void * *SGT + ) +{ + struct page ** pages = NULL; + struct page ** tmpPages = NULL; + struct sg_table *sgt = NULL; + struct mem_block *memBlk = MemBlk; + + int status = 0; + size_t offset = Offset & ~PAGE_MASK; /* Offset to the first page */ + size_t skipPages = Offset >> PAGE_SHIFT; /* skipped pages */ + size_t numPages = (PAGE_ALIGN(Offset + Bytes) >> PAGE_SHIFT) - skipPages; + size_t i; + + if (memBlk->contiguous) + { + ONERROR(AllocateMemory(sizeof(struct page*) * numPages, (void * *)&tmpPages)); + pages = tmpPages; + + for (i = 0; i < numPages; ++i) + { + pages[i] = nth_page(memBlk->contiguousPages, i + skipPages); + } + } + else + { + pages = &memBlk->nonContiguousPages[skipPages]; + } + + ONERROR(AllocateMemory(sizeof(struct sg_table) * numPages, (void * *)&sgt)); + + if (sg_alloc_table_from_pages(sgt, pages, numPages, offset, Bytes, GFP_KERNEL) < 0) + { + ONERROR(EPERM); + } + + *SGT = (void *)sgt; + +OnError: + if (tmpPages) + { + FreeMemory(tmpPages); + } + + if (IS_ERROR(status) && sgt) + { + FreeMemory(sgt); + } + + return status; +} + +static int +Mmap( + IN struct mem_block *MemBlk, + IN size_t skipPages, + IN size_t numPages, + IN struct vm_area_struct *vma + ) +{ + struct mem_block *memBlk = MemBlk; + int status = 0; + + vma->vm_flags |= VM_FLAGS; + + /* Make this mapping non-cached. */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + /* Now map all the vmalloc pages to this user address. */ + if (memBlk->contiguous) + { + /* map kernel memory to user space.. */ + if (remap_pfn_range(vma, + vma->vm_start, + page_to_pfn(memBlk->contiguousPages) + skipPages, + numPages << PAGE_SHIFT, + vma->vm_page_prot) < 0) + { + ONERROR(ENOMEM); + } + } + else + { + size_t i; + unsigned long start = vma->vm_start; + + for (i = 0; i < numPages; ++i) + { + unsigned long pfn = page_to_pfn(memBlk->nonContiguousPages[i + skipPages]); + + if (remap_pfn_range(vma, + start, + pfn, + PAGE_SIZE, + vma->vm_page_prot) < 0) + { + ONERROR(ENOMEM); + } + + start += PAGE_SIZE; + } + } + +OnError: + return status; +} + +static int +Attach( + INOUT struct mem_block *MemBlk + ) +{ + int status; + struct mem_block *memBlk = MemBlk; + + struct dma_buf *dmabuf = memBlk->dmabuf; + struct sg_table *sgt = NULL; + struct dma_buf_attachment *attachment = NULL; + int npages = 0; + unsigned long *pagearray = NULL; + int i, j, k = 0; + struct scatterlist *s; + unsigned int size = 0; + + if (!dmabuf) + { + ONERROR(EFAULT); + } + + get_dma_buf(dmabuf); + attachment = dma_buf_attach(dmabuf, gdev); + + if (!attachment) + { + ONERROR(EFAULT); + } + + sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + + if (!sgt) + { + ONERROR(EFAULT); + } + + /* Prepare page array. */ + /* Get number of pages. */ + for_each_sg(sgt->sgl, s, sgt->orig_nents, i) + { + npages += (sg_dma_len(s) + PAGE_SIZE - 1) / PAGE_SIZE; + } + + /* Allocate page array. */ + ONERROR(AllocateMemory(npages * sizeof(*pagearray), (void * *)&pagearray)); + + /* Fill page array. */ + for_each_sg(sgt->sgl, s, sgt->orig_nents, i) + { + for (j = 0; j < (sg_dma_len(s) + PAGE_SIZE - 1) / PAGE_SIZE; j++) + { + pagearray[k++] = sg_dma_address(s) + j * PAGE_SIZE; + } + size = sg_dma_len(s); + } + + memBlk->pagearray = pagearray; + memBlk->attachment = attachment; + memBlk->sgt = sgt; + memBlk->numPages = npages; + memBlk->size = size; + memBlk->contiguous = (sgt->nents == 1) ? true : false; + + return 0; + +OnError: + if (pagearray) + { + FreeMemory(pagearray); + pagearray = NULL; + } + + if (sgt) + { + dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL); + } + + return status; +} + +static struct sg_table *_dmabuf_map(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct sg_table *sgt = NULL; + struct dma_buf *dmabuf = attachment->dmabuf; + struct mem_block *memBlk = dmabuf->priv; + int status = 0; + + printk("%s\n", __func__); + + do + { + ERR_BREAK(GetSGT(memBlk, 0, memBlk->size, (void **)&sgt)); + + if (dma_map_sg(attachment->dev, sgt->sgl, sgt->nents, direction) == 0) + { + sg_free_table(sgt); + kfree(sgt); + sgt = NULL; + ERR_BREAK(EPERM); + } + } + while (false); + + return sgt; +} + +static void _dmabuf_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sgt, + enum dma_data_direction direction) +{ + dma_unmap_sg(attachment->dev, sgt->sgl, sgt->nents, direction); + + sg_free_table(sgt); + kfree(sgt); +} + +static int _dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct mem_block *memBlk = dmabuf->priv; + size_t skipPages = vma->vm_pgoff; + size_t numPages = PAGE_ALIGN(vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + int status = 0; + + printk("%s, %d: 0x%llx\n", __func__, __LINE__, page_to_phys(nth_page(memBlk->contiguousPages, 0))); + + ONERROR(Mmap(memBlk, skipPages, numPages, vma)); + +OnError: + return IS_ERROR(status) ? -EINVAL : 0; +} + +static void _dmabuf_release(struct dma_buf *dmabuf) +{ +} + +static void *_dmabuf_kmap(struct dma_buf *dmabuf, unsigned long offset) +{ + char *kvaddr = NULL; + + return (void *)kvaddr; +} + +static void _dmabuf_kunmap(struct dma_buf *dmabuf, unsigned long offset, void *ptr) +{ +} + +static struct dma_buf_ops _dmabuf_ops = +{ + .map_dma_buf = _dmabuf_map, + .unmap_dma_buf = _dmabuf_unmap, + .mmap = _dmabuf_mmap, + .release = _dmabuf_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0) + .map = _dmabuf_kmap, + .unmap = _dmabuf_kunmap, +#endif +}; + + +static int +DMABUF_Export( + IN struct file *filp, + IN unsigned long bus_address, + IN signed int Flags, + OUT signed int *FD + ) +{ + int status = 0; + struct dma_buf *dmabuf = NULL; + struct mem_block *memBlk = NULL; + struct mem_node *mnode = NULL; + + mnode = get_mem_node(filp, bus_address, 0); + if (NULL == mnode) + { + printk("Allocator: Cannot find mem_node with bus address 0x%lx\n", bus_address); + ONERROR(EINVAL); + } + + memBlk = &mnode->memBlk; + + dmabuf = memBlk->dmabuf; + if (dmabuf == NULL) + { + size_t bytes = memBlk->size; + + { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + exp_info.ops = &_dmabuf_ops; + exp_info.size = bytes; + exp_info.flags = Flags; + exp_info.priv = memBlk; + dmabuf = dma_buf_export(&exp_info); + } + + if (dmabuf == NULL) + { + ONERROR(EFAULT); + } + + memBlk->dmabuf = dmabuf; + } + + if (FD) + { + int fd = dma_buf_fd(dmabuf, Flags); + + if (fd < 0) + { + ONERROR(EIO); + } + + *FD = fd; + } + +OnError: + return status; +} + +static int +DMABUF_Import( + IN struct file *filp, + IN signed int FD, + OUT unsigned long *bus_address, + OUT unsigned int *size + ) +{ + int status; + + struct mem_block *memBlk = NULL; + struct file_node *fnode = NULL; + struct mem_node *mnode = NULL; + + mnode = kzalloc(sizeof(struct mem_node), GFP_KERNEL | __GFP_NORETRY); + + if (!mnode) + { + ONERROR(ENOMEM); + } + + fnode = get_file_node(filp); + + if (NULL == fnode) + { + ONERROR(EINVAL); + } + + memBlk = &mnode->memBlk; + + /* Import dma buf handle. */ + memBlk->dmabuf = dma_buf_get(FD); + if (!memBlk->dmabuf) + { + ONERROR(EFAULT); + } + + ONERROR(Attach(memBlk)); + + *bus_address = memBlk->pagearray[0]; + *size = memBlk->size; + + mnode->busAddr = memBlk->pagearray[0]; + mnode->isImported = 1; + spin_lock(&mem_lock); + list_add_tail(&mnode->link, &fnode->memList); + spin_unlock(&mem_lock); + + return 0; + +OnError: + if (mnode) + { + kfree(mnode); + } + + + return status; +} + +void +DMABUF_Release( + IN struct file *filp, + IN unsigned long bus_address + ) +{ + struct mem_block *memBlk = NULL; + struct mem_node *mnode = NULL; + + mnode = get_mem_node(filp, bus_address, 1); + if (NULL == mnode) + { + return; + } + + memBlk = &mnode->memBlk; + + dma_buf_unmap_attachment(memBlk->attachment, memBlk->sgt, DMA_BIDIRECTIONAL); + + dma_buf_detach(memBlk->dmabuf, memBlk->attachment); + + dma_buf_put(memBlk->dmabuf); + + FreeMemory(memBlk->pagearray); + + spin_lock(&mem_lock); + list_del(&mnode->link); + spin_unlock(&mem_lock); + kfree(mnode); +} + +/***************************************************************************\ +************************ GFP Allocator ********************************** +\***************************************************************************/ +static void +NonContiguousFree( + IN struct page ** Pages, + IN size_t NumPages + ) +{ + size_t i; + + for (i = 0; i < NumPages; i++) + { + __free_page(Pages[i]); + } + + FreeMemory(Pages); +} + +static int +NonContiguousAlloc( + IN struct mem_block *MemBlk, + IN size_t NumPages, + IN unsigned int Gfp + ) +{ + struct page ** pages; + struct page *p; + size_t i, size; + + if (NumPages > totalram_pages()) + { + return ENOMEM; + } + + size = NumPages * sizeof(struct page *); + if (AllocateMemory(size, (void * *)&pages)) + return ENOMEM; + + for (i = 0; i < NumPages; i++) + { + p = alloc_page(Gfp); + + if (!p) + { + NonContiguousFree(pages, i); + return ENOMEM; + } + +#if DISCRETE_PAGES + if (i != 0) + { + if (page_to_pfn(pages[i-1]) == page_to_pfn(p)-1) + { + /* Replaced page. */ + struct page *l = p; + + /* Allocate a page which is not contiguous to previous one. */ + p = alloc_page(Gfp); + + /* Give replaced page back. */ + __free_page(l); + + if (!p) + { + NonContiguousFree(pages, i); + return ENOMEM; + } + } + } +#endif + + pages[i] = p; + } + + MemBlk->nonContiguousPages = pages; + + return 0; +} + +static int +getPhysical( + IN struct mem_block *MemBlk, + IN unsigned int Offset, + OUT unsigned long * Physical + ) +{ + struct mem_block *memBlk = MemBlk; + unsigned int offsetInPage = Offset & ~PAGE_MASK; + unsigned int index = Offset / PAGE_SIZE; + + if (memBlk->contiguous) + { + *Physical = page_to_phys(nth_page(memBlk->contiguousPages, index)); + } + else + { + *Physical = page_to_phys(memBlk->nonContiguousPages[index]); + } + + *Physical += offsetInPage; + + return 0; +} + +int +GFP_Alloc( + IN struct file *filp, + IN unsigned int size, + IN unsigned int Flags, + OUT unsigned long *bus_address + ) +{ + int status; + size_t i = 0; + unsigned int gfp = GFP_KERNEL | __GFP_HIGHMEM | __GFP_NOWARN; + int contiguous = Flags & ALLOC_FLAG_CONTIGUOUS; + size_t numPages = GetPageCount(size, 0); + unsigned long physical = 0; + + struct mem_block *memBlk = NULL; + struct file_node *fnode = NULL; + struct mem_node *mnode = NULL; + + mnode = kzalloc(sizeof(struct mem_node), GFP_KERNEL | __GFP_NORETRY); + + if (!mnode) + { + ONERROR(ENOMEM); + } + + fnode = get_file_node(filp); + + if (NULL == fnode) + { + ONERROR(EINVAL); + } + + memBlk = &mnode->memBlk; + + if (Flags & ALLOC_FLAG_4GB_ADDR) + { + /* remove __GFP_HIGHMEM bit, add __GFP_DMA32 bit */ + gfp &= ~__GFP_HIGHMEM; + gfp |= __GFP_DMA32; + } + + if (contiguous) + { + size_t bytes = numPages << PAGE_SHIFT; + + void *addr = NULL; + + addr = alloc_pages_exact(bytes, (gfp & ~__GFP_HIGHMEM) | __GFP_NORETRY); + + memBlk->contiguousPages = addr ? virt_to_page(addr) : NULL; + + if (memBlk->contiguousPages) + { + memBlk->exact = true; + } + + if (memBlk->contiguousPages == NULL) + { + int order = get_order(bytes); + + if (order >= MAX_ORDER) + { + status = ENOMEM; + goto OnError; + } + + memBlk->contiguousPages = alloc_pages(gfp, order); + } + + if (memBlk->contiguousPages == NULL) + { + ONERROR(ENOMEM); + } + + memBlk->dma_addr = dma_map_page(gdev, + memBlk->contiguousPages, 0, numPages * PAGE_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(gdev, memBlk->dma_addr)) + { + if (memBlk->exact) + { + free_pages_exact(page_address(memBlk->contiguousPages), bytes); + } + else + { + __free_pages(memBlk->contiguousPages, get_order(bytes)); + } + + ONERROR(ENOMEM); + } + } + else // non-contiguous pages + { + ONERROR(NonContiguousAlloc(memBlk, numPages, gfp)); + + // this piece of code is for sanity check + memBlk->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL | __GFP_NORETRY); + if (!memBlk->sgt) + { + ONERROR(ENOMEM); + } + + status = sg_alloc_table_from_pages(memBlk->sgt, + memBlk->nonContiguousPages, numPages, 0, + numPages << PAGE_SHIFT, GFP_KERNEL); + memBlk->sgt->orig_nents = memBlk->sgt->nents; + if (status < 0) + { + NonContiguousFree(memBlk->nonContiguousPages, numPages); + ONERROR(ENOMEM); + } + + status = dma_map_sg(gdev, memBlk->sgt->sgl, memBlk->sgt->nents, DMA_BIDIRECTIONAL); + if (status != memBlk->sgt->nents) + { + NonContiguousFree(memBlk->nonContiguousPages, numPages); + sg_free_table(memBlk->sgt); + ONERROR(ENOMEM); + } + } + + for (i = 0; i < numPages; i++) + { + struct page *page; + + if (contiguous) + { + page = nth_page(memBlk->contiguousPages, i); + } + else + { + page = memBlk->nonContiguousPages[i]; + } + + SetPageReserved(page); + } + + memBlk->contiguous = contiguous; + memBlk->numPages = numPages; + memBlk->size = size; + + getPhysical(memBlk, 0, &physical); + *bus_address = physical; + mnode->busAddr = physical; + mnode->isImported = 0; + + list_add_tail(&mnode->link, &fnode->memList); + + printk("Allocated %d bytes (%ld pages) at physical address 0x%lx with %d sg table entries\n", + size, numPages, physical, contiguous ? 1 : memBlk->sgt->nents); + + return 0; + +OnError: + if (mnode) + { + kfree(mnode); + } + + if (memBlk->sgt) + { + kfree(memBlk->sgt); + } + + return status; +} + + +void +GFP_Free( + IN struct file *filp, + IN unsigned long bus_address + ) +{ + size_t i; + struct page * page; + struct mem_block *memBlk = NULL; + struct mem_node *mnode = NULL; + + mnode = get_mem_node(filp, bus_address, 0); + if (NULL == mnode) + { + return; + } + + memBlk = &mnode->memBlk; + + printk("Free %ld pages from physical address 0x%lx\n", memBlk->numPages, mnode->busAddr); + + if (memBlk->contiguous) + { + dma_unmap_page(gdev, memBlk->dma_addr, + memBlk->numPages << PAGE_SHIFT, DMA_FROM_DEVICE); + } + else + { + dma_unmap_sg(gdev, memBlk->sgt->sgl, memBlk->sgt->nents, + DMA_FROM_DEVICE); + + sg_free_table(memBlk->sgt); + + if (memBlk->sgt) + { + kfree(memBlk->sgt); + } + } + + for (i = 0; i < memBlk->numPages; i++) + { + if (memBlk->contiguous) + { + page = nth_page(memBlk->contiguousPages, i); + + ClearPageReserved(page); + } + } + + if (memBlk->contiguous) + { + if (memBlk->exact == true) + { + free_pages_exact(page_address(memBlk->contiguousPages), memBlk->numPages * PAGE_SIZE); + } + else + { + __free_pages(memBlk->contiguousPages, get_order(memBlk->numPages * PAGE_SIZE)); + } + } + else + { + NonContiguousFree(memBlk->nonContiguousPages, memBlk->numPages); + } + + list_del(&mnode->link); + kfree(mnode); +} + +static int +GFP_MapUser( + IN struct file *filp, + IN struct vm_area_struct *vma + ) +{ + struct mem_block *memBlk = NULL; + struct mem_node *mnode = NULL; + unsigned long bus_address = vma->vm_pgoff * PAGE_SIZE; + int status = 0; + + mnode = get_mem_node(filp, bus_address, 0); + if (NULL == mnode) + { + return EINVAL; + } + + memBlk = &mnode->memBlk; + + if (Mmap(memBlk, 0, memBlk->numPages, vma)) + { + return ENOMEM; + } + + memBlk->vma = vma; + + DEBUG_PRINT("Map %ld pages from physical address 0x%lx\n", memBlk->numPages, mnode->busAddr); + + return status; +} + +int allocator_mmap(struct file *filp, struct vm_area_struct *vma) +{ + return GFP_MapUser(filp, vma); +} + +int allocator_ioctl(void *filp, unsigned int cmd, unsigned long arg) +{ + MemallocParams params; + int ret = 0; + + if (_IOC_TYPE(cmd) != MEMORY_IOC_MAGIC) return EINVAL; + if (_IOC_NR(cmd) > MEMORY_IOC_MAXNR) return EINVAL; + + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(arg, _IOC_SIZE(cmd)); + if (ret) return EINVAL; + + switch (cmd) + { + case MEMORY_IOC_ALLOCATE: + { + ret = copy_from_user(¶ms, (void*)arg, sizeof(MemallocParams)); + if (!ret) + { + ret = GFP_Alloc(filp, params.size, params.flags, ¶ms.bus_address); + params.translation_offset = 0; + if (!ret) + ret = copy_to_user((MemallocParams *)arg, ¶ms, sizeof(MemallocParams)); + } + break; + } + case MEMORY_IOC_FREE: + { + ret = copy_from_user(¶ms, (void*)arg, sizeof(MemallocParams)); + if (!ret) + { + GFP_Free(filp, params.bus_address); + ret = copy_to_user((MemallocParams *)arg, ¶ms, sizeof(MemallocParams)); + } + break; + } + case MEMORY_IOC_DMABUF_EXPORT: + { + ret = copy_from_user(¶ms, (void*)arg, sizeof(MemallocParams)); + if (!ret) + { + ret = DMABUF_Export(filp, params.bus_address, params.flags, ¶ms.fd); + if (!ret) + ret = copy_to_user((MemallocParams *)arg, ¶ms, sizeof(MemallocParams)); + } + break; + } + case MEMORY_IOC_DMABUF_IMPORT: + { + ret = copy_from_user(¶ms, (void*)arg, sizeof(MemallocParams)); + if (!ret) + { + ret = DMABUF_Import(filp, params.fd, ¶ms.bus_address, ¶ms.size); + params.translation_offset = 0; + if (!ret) + ret = copy_to_user((MemallocParams *)arg, ¶ms, sizeof(MemallocParams)); + } + break; + } + case MEMORY_IOC_DMABUF_RELEASE: + { + ret = copy_from_user(¶ms, (void*)arg, sizeof(MemallocParams)); + if (!ret) + { + DMABUF_Release(filp, params.bus_address); + ret = copy_to_user((MemallocParams *)arg, ¶ms, sizeof(MemallocParams)); + } + break; + } + default: + ret = EINVAL; + } + + return ret; +} + + +int allocator_init(struct device *dev) +{ + int ret = 0; + + gdev = dev; + INIT_LIST_HEAD(&fileList); + + return ret; +} + +void allocator_remove(void) +{ +} + +int allocator_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct file_node *fnode = NULL; + + if (AllocateMemory(sizeof(struct file_node), (void * *)&fnode)) + return ENOMEM; + + fnode->filp = filp; + INIT_LIST_HEAD(&fnode->memList); + spin_lock(&mem_lock); + list_add_tail(&fnode->link, &fileList); + spin_unlock(&mem_lock); + + return ret; +} + +void allocator_release(struct inode *inode, struct file *filp) +{ +#if 0 + struct file_node *fnode = find_and_delete_file_node(filp); + struct mem_node *node; + struct mem_node *temp; + + if (NULL == fnode) + return; + + list_for_each_entry_safe(node, temp, &fnode->memList, link) + { + // this is not expected, memory leak detected! + printk("Allocator: Found unfreed memory at 0x%lx\n", node->busAddr); + if (node->isImported) + DMABUF_Release(filp, node->busAddr); + else + GFP_Free(filp, node->busAddr); + list_del(&node->link); + FreeMemory(node); + } + + list_del(&fnode->link); + FreeMemory(fnode); +#endif +} diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.h new file mode 100644 index 000000000..688075923 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/kernel_allocator.h @@ -0,0 +1,121 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 Vivante Corporation +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_allocator_h_ +#define __gc_hal_kernel_allocator_h_ + +#include "../memalloc/memalloc.h" + + +#define IN +#define OUT +#define INOUT +#define OPTIONAL + +/* No special needs. */ +#define ALLOC_FLAG_NONE 0x00000000 +/* Physical contiguous. */ +#define ALLOC_FLAG_CONTIGUOUS 0x00000001 +/* Physical non contiguous. */ +#define ALLOC_FLAG_NON_CONTIGUOUS 0x00000002 +/* Need 32bit address. */ +#define ALLOC_FLAG_4GB_ADDR 0x00000004 + + +#define MEMORY_IOC_MAGIC 'a' + +#define MEMORY_IOC_ALLOCATE _IOWR(MEMORY_IOC_MAGIC, 1, MemallocParams *) +#define MEMORY_IOC_FREE _IOWR(MEMORY_IOC_MAGIC, 2, MemallocParams *) +#define MEMORY_IOC_DMABUF_EXPORT _IOWR(MEMORY_IOC_MAGIC, 3, MemallocParams *) +#define MEMORY_IOC_DMABUF_IMPORT _IOWR(MEMORY_IOC_MAGIC, 4, MemallocParams *) +#define MEMORY_IOC_DMABUF_RELEASE _IOWR(MEMORY_IOC_MAGIC, 5, MemallocParams *) +#define MEMORY_IOC_MAXNR 5 + +#if 0 +int +GFP_Alloc( + INOUT MemallocParams *params, + IN unsigned int Flags, + IN struct device *dev + ); + +void +GFP_Free( + INOUT MemallocParams *params + ); + +int +DMABUF_Export( + IN MemallocParams *params, + IN signed int Flags, + OUT signed int *FD + ); + +int +DMABUF_Import( + IN unsigned int FD, + IN struct device *dev, + OUT MemallocParams *params + ); + +void +DMABUF_Release( + INOUT MemallocParams *params + ); +#endif + + + +#endif diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.c new file mode 100644 index 000000000..cb739e952 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.c @@ -0,0 +1,203 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include "subsys.h" + +/******************************************************************************/ +/* subsystem configuration */ +/******************************************************************************/ + +/* List of subsystems */ +struct SubsysDesc subsys_array[] = { + /* {slice_index, index, base} */ + {0, 0, 0xffecc00000}, +// {0, 1, 0x700000} +}; + +/* List of all HW cores. */ +struct CoreDesc core_array[] = { + /* {slice, subsys, core_type, offset, iosize, irq, has_apbfilter} */ +#if 0 + {0, 0, HW_VC8000DJ, 0x600000, 0, 0}, + {0, 0, HW_VC8000D, 0x602000, 0, 0}, + {0, 0, HW_L2CACHE, 0x604000, 0, 0}, + {0, 0, HW_DEC400, 0x606000, 0, 0}, + {0, 0, HW_BIGOCEAN, 0x608000, 0, 0}, + {0, 0, HW_NOC,0x60a000, 0, 0}, + {0, 0, HW_AXIFE, 0x60c000, 0, 0} +#endif + {0, 0, HW_VCMD, 0x0, 27*4, 12}, + {0, 0, HW_VC8000D, 0x1000, 1023*4, -1, 0}, + {0, 0, HW_L2CACHE, 0x2000, 231*4, -1, 0}, + {0, 0, HW_MMU, 0x3000, 228*4, -1, 0}, + //{0, 0, HW_MMU_WR, 0x4000, 228*4, -1, 0}, + //{0, 0, HW_AXIFE, 0x5000, 64*4, -1, 1}, + {0, 0, HW_DEC400, 0x6000, 1568*4, -1, 0}, + //{0, 1, HW_VCMD, 0x0, 27*4, -1, 0}, + //{0, 1, HW_VC8000D, 0x1000, 503*4, -1, 1}, + //{0, 1, HW_L2CACHE, 0x2000, 231*4, -1, 0}, + //{0, 1, HW_MMU, 0x3000, 228*4, -1, 0}, + //{0, 1, HW_MMU_WR, 0x4000, 228*4, -1, 0}, + //{0, 1, HW_AXIFE, 0x5000, 64*4, -1, 1}, + //{0, 1, HW_DEC400, 0x6000, 1568*4, -1, 0}, +}; + +extern struct vcmd_config vcmd_core_array[MAX_SUBSYS_NUM]; +extern int total_vcmd_core_num; +extern unsigned long multicorebase[]; +extern int irq[]; +extern unsigned int iosize[]; +extern int reg_count[]; + +/* + If VCMD is used, convert core_array to vcmd_core_array, which are used in + hantor_vcmd.c. + Otherwise, covnert core_array to multicore_base/irq/iosize, which are used in + hantro_dec.c + + VCMD: + - struct vcmd_config vcmd_core_array[MAX_SUBSYS_NUM] + - total_vcmd_core_num + + NON-VCMD: + - multicorebase[HXDEC_MAX_CORES] + - irq[HXDEC_MAX_CORES] + - iosize[HXDEC_MAX_CORES] +*/ +void CheckSubsysCoreArray(struct subsys_config *subsys, int *vcmd) { + int num = sizeof(subsys_array)/sizeof(subsys_array[0]); + int i, j; + + memset(subsys, 0, sizeof(subsys[0])*MAX_SUBSYS_NUM); + for (i = 0; i < num; i++) { + subsys[i].base_addr = subsys_array[i].base; + subsys[i].irq = -1; + for (j = 0; j < HW_CORE_MAX; j++) { + subsys[i].submodule_offset[j] = 0xffff; + subsys[i].submodule_iosize[j] = 0; + subsys[i].submodule_hwregs[j] = NULL; + } + } + + total_vcmd_core_num = 0; + + for (i = 0; i < sizeof(core_array)/sizeof(core_array[0]); i++) { + if (!subsys[core_array[i].subsys].base_addr) { + /* undefined subsystem */ + continue; + } + subsys[core_array[i].subsys].submodule_offset[core_array[i].core_type] + = core_array[i].offset; + subsys[core_array[i].subsys].submodule_iosize[core_array[i].core_type] + = core_array[i].iosize; + if (subsys[core_array[i].subsys].irq != -1 && core_array[i].irq != -1) { + if (subsys[core_array[i].subsys].irq != core_array[i].irq) { + printk(KERN_INFO "hantrodec: hw core type %d irq %d != subsystem irq %d\n", + core_array[i].core_type, + core_array[i].irq, + subsys[core_array[i].subsys].irq); + printk(KERN_INFO "hantrodec: hw cores of a subsystem should have same irq\n"); + } + } else if (core_array[i].irq != -1) { + subsys[core_array[i].subsys].irq = core_array[i].irq; + } + subsys[core_array[i].subsys].has_apbfilter[core_array[i].core_type] = core_array[i].has_apb; + /* vcmd found */ + if (core_array[i].core_type == HW_VCMD) { + *vcmd = 1; + total_vcmd_core_num++; + } + } + + printk(KERN_INFO "hantrodec: vcmd = %d\n", *vcmd); + + /* To plug into hantro_vcmd.c */ + if (*vcmd) { + for (i = 0; i < total_vcmd_core_num; i++) { + vcmd_core_array[i].vcmd_base_addr = subsys[i].base_addr; + vcmd_core_array[i].vcmd_iosize = subsys[i].submodule_iosize[HW_VCMD]; + vcmd_core_array[i].vcmd_irq = subsys[i].irq; + vcmd_core_array[i].sub_module_type = 2; /* TODO(min): to be fixed */ + vcmd_core_array[i].submodule_main_addr = subsys[i].submodule_offset[HW_VC8000D]; + vcmd_core_array[i].submodule_dec400_addr = subsys[i].submodule_offset[HW_DEC400]; + vcmd_core_array[i].submodule_L2Cache_addr = subsys[i].submodule_offset[HW_L2CACHE]; + vcmd_core_array[i].submodule_MMU_addr = subsys[i].submodule_offset[HW_MMU]; + vcmd_core_array[i].submodule_MMUWrite_addr = subsys[i].submodule_offset[HW_MMU_WR]; + vcmd_core_array[i].submodule_axife_addr = subsys[i].submodule_offset[HW_AXIFE]; + } + } + memset(multicorebase, 0, sizeof(multicorebase[0]) * HXDEC_MAX_CORES); + for (i = 0; i < num; i++) { + multicorebase[i] = subsys[i].base_addr + subsys[i].submodule_offset[HW_VC8000D]; + irq[i] = subsys[i].irq; + iosize[i] = subsys[i].submodule_iosize[HW_VC8000D]; + printk(KERN_INFO "hantrodec: [%d] multicorebase 0x%08lx, iosize %d\n", i, multicorebase[i], iosize[i]); + } +} + +void dump_core_array(void) +{ + int i; + for (i = 0; i < sizeof(core_array)/sizeof(core_array[0]); i++) { + printk(KERN_INFO "lucz: dumping dump_core_array[%d]\n", i); + printk(KERN_INFO " slice=%d\n", core_array[i].slice); + printk(KERN_INFO " subsys=%d\n", core_array[i].subsys); + printk(KERN_INFO " core_type=%d\n", core_array[i].core_type); + printk(KERN_INFO " offset=%d\n", core_array[i].offset); + printk(KERN_INFO " iosize=%d\n", core_array[i].iosize); + printk(KERN_INFO " irq=%d\n", core_array[i].irq); + printk(KERN_INFO " has_apb=%d\n", core_array[i].has_apb); + } +} + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.h new file mode 100644 index 000000000..b74fc2d69 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/subsys.h @@ -0,0 +1,204 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _SUBSYS_H_ +#define _SUBSYS_H_ + +#ifdef __FREERTOS__ +/* nothing */ +#elif defined(__linux__) +#include +#include +#endif +#include "hantrodec.h" + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +/* Functions provided by all other subsystem IP - hantrodec_xxx.c */ + +/******************************************************************************/ +/* subsys level */ +/******************************************************************************/ + +#define MAX_SUBSYS_NUM 4 /* up to 4 subsystem (temporary) */ +#define HXDEC_MAX_CORES MAX_SUBSYS_NUM /* used in hantro_dec_xxx.c */ + +/* SubsysDesc & CoreDesc are used for configuration */ +struct SubsysDesc { + int slice_index; /* slice this subsys belongs to */ + int index; /* subsystem index */ + long base; +}; + +struct CoreDesc { + int slice; + int subsys; /* subsys this core belongs to */ + enum CoreType core_type; + int offset; /* offset to subsystem base */ + int iosize; + int irq; + int has_apb; +}; + +/* internal config struct (translated from SubsysDesc & CoreDesc) */ +struct subsys_config { + unsigned long base_addr; + int irq; + u32 subsys_type; /* identifier for each subsys vc8000e=0,IM=1,vc8000d=2,jpege=3,jpegd=4 */ + u32 submodule_offset[HW_CORE_MAX]; /* in bytes */ + u16 submodule_iosize[HW_CORE_MAX]; /* in bytes */ + volatile u8 *submodule_hwregs[HW_CORE_MAX]; /* virtual address */ + int has_apbfilter[HW_CORE_MAX]; +}; + +void CheckSubsysCoreArray(struct subsys_config *subsys, int *vcmd); + +/******************************************************************************/ +/* VCMD */ +/******************************************************************************/ +#define OPCODE_WREG (0x01<<27) +#define OPCODE_END (0x02<<27) +#define OPCODE_NOP (0x03<<27) +#define OPCODE_RREG (0x16<<27) +#define OPCODE_INT (0x18<<27) +#define OPCODE_JMP (0x19<<27) +#define OPCODE_STALL (0x09<<27) +#define OPCODE_CLRINT (0x1a<<27) +#define OPCODE_JMP_RDY0 (0x19<<27) +#define OPCODE_JMP_RDY1 ((0x19<<27)|(1<<26)) +#define JMP_IE_1 (1<<25) +#define JMP_RDY_1 (1<<26) + +/* Used in vcmd initialization in hantro_vcmd_xxx.c. */ +/* May be unified in next step. */ +struct vcmd_config { + unsigned long vcmd_base_addr; + u32 vcmd_iosize; + int vcmd_irq; + u32 sub_module_type; /*input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4*/ + u16 submodule_main_addr; // in byte + u16 submodule_dec400_addr; //if submodule addr == 0xffff, this submodule does not exist.// in byte + u16 submodule_L2Cache_addr; // in byte + u16 submodule_MMU_addr; // in byte + u16 submodule_MMUWrite_addr;// in byte + u16 submodule_axife_addr; // in byte +}; + + +#ifdef __FREERTOS__ +/* nothing */ +#elif defined(__linux__) +int hantrovcmd_open(struct inode *inode, struct file *filp); +int hantrovcmd_release(struct inode *inode, struct file *filp); +long hantrovcmd_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg); +int hantrovcmd_init(struct platform_device *pdev); +void hantrovcmd_cleanup(struct platform_device *pdev); +void hantrovcmd_reset(void); +bool hantro_cmdbuf_range(addr_t addr,size_t size); + +/******************************************************************************/ +/* MMU */ +/******************************************************************************/ + +/* Init MMU, should be called in driver init function. */ +enum MMUStatus MMUInit(volatile unsigned char *hwregs); +/* Clean up all data in MMU, should be called in driver cleanup function + when rmmod driver*/ +enum MMUStatus MMUCleanup(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); +/* The function should be called in driver realease function + when driver exit unnormally */ +enum MMUStatus MMURelease(void *filp, volatile unsigned char *hwregs); + +enum MMUStatus MMUEnable(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +/* Used in kernel to map buffer */ +enum MMUStatus MMUKernelMemNodeMap(struct kernel_addr_desc *addr); +/* Used in kernel to unmap buffer */ +enum MMUStatus MMUKernelMemNodeUnmap(struct kernel_addr_desc *addr); + +long MMUIoctl(unsigned int cmd, void *filp, unsigned long arg, + volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +void MMURestore(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +int allocator_init(struct device *dev); +void allocator_remove(void); +int allocator_open(struct inode *inode, struct file *filp); +void allocator_release(struct inode *inode, struct file *filp); +int allocator_ioctl(void *filp, unsigned int cmd, unsigned long arg); +int allocator_mmap(struct file *filp, struct vm_area_struct *vma); + +/******************************************************************************/ +/* L2Cache */ +/******************************************************************************/ + +/******************************************************************************/ +/* DEC400 */ +/******************************************************************************/ + + +/******************************************************************************/ +/* AXI FE */ +/******************************************************************************/ +#endif + +#endif + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregisterenum.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregisterenum.h new file mode 100644 index 000000000..0bf7e43e1 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregisterenum.h @@ -0,0 +1,156 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/* Register interface based on the document version 1.1.2 */ + HWIF_VCMD_HW_ID, + HWIF_VCMD_HW_VERSION, + HWIF_VCMD_HW_BUILD_DATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE, + HWIF_VCMD_EXE_CMDBUF_COUNT, + HWIF_VCMD_EXECUTING_CMD, + HWIF_VCMD_EXECUTING_CMD_MSB, + HWIF_VCMD_AXI_TOTAL_AR_LEN, + HWIF_VCMD_AXI_TOTAL_R, + HWIF_VCMD_AXI_TOTAL_AR, + HWIF_VCMD_AXI_TOTAL_R_LAST, + HWIF_VCMD_AXI_TOTAL_AW_LEN, + HWIF_VCMD_AXI_TOTAL_W, + HWIF_VCMD_AXI_TOTAL_AW, + HWIF_VCMD_AXI_TOTAL_W_LAST, + HWIF_VCMD_AXI_TOTAL_B, + HWIF_VCMD_AXI_AR_VALID, + HWIF_VCMD_AXI_AR_READY, + HWIF_VCMD_AXI_R_VALID, + HWIF_VCMD_AXI_R_READY, + HWIF_VCMD_AXI_AW_VALID, + HWIF_VCMD_AXI_AW_READY, + HWIF_VCMD_AXI_W_VALID, + HWIF_VCMD_AXI_W_READY, + HWIF_VCMD_AXI_B_VALID, + HWIF_VCMD_AXI_B_READY, + HWIF_VCMD_WORK_STATE, + HWIF_VCMD_AXI_CLK_GATE_DISABLE, + HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE, + HWIF_VCMD_CORE_CLK_GATE_DISABLE, + HWIF_VCMD_ABORT_MODE, + HWIF_VCMD_RESET_CORE, + HWIF_VCMD_RESET_ALL, + HWIF_VCMD_START_TRIGGER, + HWIF_VCMD_IRQ_INTCMD, + HWIF_VCMD_IRQ_JMPP, + HWIF_VCMD_IRQ_JMPD, + HWIF_VCMD_IRQ_RESET, + HWIF_VCMD_IRQ_ABORT, + HWIF_VCMD_IRQ_CMDERR, + HWIF_VCMD_IRQ_TIMEOUT, + HWIF_VCMD_IRQ_BUSERR, + HWIF_VCMD_IRQ_ENDCMD, + HWIF_VCMD_IRQ_INTCMD_EN, + HWIF_VCMD_IRQ_JMPP_EN, + HWIF_VCMD_IRQ_JMPD_EN, + HWIF_VCMD_IRQ_RESET_EN, + HWIF_VCMD_IRQ_ABORT_EN, + HWIF_VCMD_IRQ_CMDERR_EN, + HWIF_VCMD_IRQ_TIMEOUT_EN, + HWIF_VCMD_IRQ_BUSERR_EN, + HWIF_VCMD_IRQ_ENDCMD_EN, + HWIF_VCMD_TIMEOUT_EN, + HWIF_VCMD_TIMEOUT_CYCLES, + HWIF_VCMD_EXECUTING_CMD_ADDR, + HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, + HWIF_VCMD_EXE_CMDBUF_LENGTH, + HWIF_VCMD_CMD_SWAP, + HWIF_VCMD_MAX_BURST_LEN, + HWIF_VCMD_AXI_ID_RD, + HWIF_VCMD_AXI_ID_WR, + HWIF_VCMD_RDY_CMDBUF_COUNT, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_GATE, + HWIF_VCMD_CMDBUF_EXECUTING_ID, diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregistertable.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregistertable.h new file mode 100644 index 000000000..f0a009d4b --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdregistertable.h @@ -0,0 +1,156 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/* Register interface based on the document version 1.1.2 */ + VCMDREG(HWIF_VCMD_HW_ID , 0 ,0xffff0000, 16, 0,RO,"HW ID"), + VCMDREG(HWIF_VCMD_HW_VERSION , 0 ,0x0000ffff, 0, 0,RO,"version of hw(1.0.0).[15:12]-major [11:8]-minor [7:0]-build"), + VCMDREG(HWIF_VCMD_HW_BUILD_DATE , 4 ,0xffffffff, 0, 0,RO,"Hw package generation date in BCD code"), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU , 8 ,0x08000000, 27, 0,RO,"external abnormal interrupt source from mmu of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE , 8 ,0x04000000, 26, 0,RO,"external abnormal interrupt source from l2cache of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400 , 8 ,0x02000000, 25, 0,RO,"external abnormal interrupt source from dec400 of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD , 8 ,0x01000000, 24, 0,RO,"external abnormal interrupt source from vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU , 8 ,0x00200000, 21, 0,RO,"external abnormal interrupt source from mmu of cutree."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU , 8 ,0x00100000, 20, 0,RO,"external abnormal interrupt source from mmu of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE , 8 ,0x00080000, 19, 0,RO,"external abnormal interrupt source from l2 cache of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400 , 8 ,0x00040000, 18, 0,RO,"external abnormal interrupt source from dec400 of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE , 8 ,0x00020000, 17, 0,RO,"external abnormal interrupt source from cutree."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE , 8 ,0x00010000, 16, 0,RO,"external abnormal interrupt source from vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU , 8 ,0x00000800, 11, 0,RO,"external normal interrupt source from mmu of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE , 8 ,0x00000400, 10, 0,RO,"external normal interrupt source from l2cache of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400 , 8 ,0x00000200, 9, 0,RO,"external normal interrupt source from dec400 of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD , 8 ,0x00000100, 8, 0,RO,"external normal interrupt source from vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU , 8 ,0x00000020, 5, 0,RO,"external normal interrupt source from mmu of cutree."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU , 8 ,0x00000010, 4, 0,RO,"external normal interrupt source from mmu of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE , 8 ,0x00000008, 3, 0,RO,"external normal interrupt source from l2 cache of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400 , 8 ,0x00000004, 2, 0,RO,"external normal interrupt source from dec400 of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE , 8 ,0x00000002, 1, 0,RO,"external normal interrupt source from cutree."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE , 8 ,0x00000001, 0, 0,RO,"external normal interrupt source from vce."), + VCMDREG(HWIF_VCMD_EXE_CMDBUF_COUNT , 12 ,0xffffffff, 0, 0,RO,"Hw increases this counter by 1 after one more command buffer has been executed"), + VCMDREG(HWIF_VCMD_EXECUTING_CMD , 16 ,0xffffffff, 0, 0,RO,"the first 32 bits of the executing cmd."), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_MSB , 20 ,0xffffffff, 0, 0,RO,"the second 32 bits of the executing cmd."), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AR_LEN , 24 ,0xffffffff, 0, 0,RO,"axi total ar length"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_R , 28 ,0xffffffff, 0, 0,RO,"axi total r"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AR , 32 ,0xffffffff, 0, 0,RO,"axi total ar"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_R_LAST , 36 ,0xffffffff, 0, 0,RO,"axi total r last"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AW_LEN , 40 ,0xffffffff, 0, 0,RO,"axi total aw length"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_W , 44 ,0xffffffff, 0, 0,RO,"axi total w"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AW , 48 ,0xffffffff, 0, 0,RO,"axi total aw"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_W_LAST , 52 ,0xffffffff, 0, 0,RO,"axi total w last"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_B , 56 ,0xffffffff, 0, 0,RO,"axi total b"), + VCMDREG(HWIF_VCMD_AXI_AR_VALID , 60 ,0x80000000, 31, 0,RO,"axi ar valid"), + VCMDREG(HWIF_VCMD_AXI_AR_READY , 60 ,0x40000000, 30, 0,RO,"axi ar ready"), + VCMDREG(HWIF_VCMD_AXI_R_VALID , 60 ,0x20000000, 29, 0,RO,"axi r valid"), + VCMDREG(HWIF_VCMD_AXI_R_READY , 60 ,0x10000000, 28, 0,RO,"axi r ready"), + VCMDREG(HWIF_VCMD_AXI_AW_VALID , 60 ,0x08000000, 27, 0,RO,"axi aw valid"), + VCMDREG(HWIF_VCMD_AXI_AW_READY , 60 ,0x04000000, 26, 0,RO,"axi aw ready"), + VCMDREG(HWIF_VCMD_AXI_W_VALID , 60 ,0x02000000, 25, 0,RO,"axi w valid"), + VCMDREG(HWIF_VCMD_AXI_W_READY , 60 ,0x01000000, 24, 0,RO,"axi w ready"), + VCMDREG(HWIF_VCMD_AXI_B_VALID , 60 ,0x00800000, 23, 0,RO,"axi b valid"), + VCMDREG(HWIF_VCMD_AXI_B_READY , 60 ,0x00400000, 22, 0,RO,"axi b ready"), + VCMDREG(HWIF_VCMD_WORK_STATE , 60 ,0x00000007, 0, 0,RO,"hw work state. 0-IDLE 1-WORK 2-STALL 3-PEND 4-ABORT"), + VCMDREG(HWIF_VCMD_AXI_CLK_GATE_DISABLE , 64 ,0x00000040, 6, 0,RW,"keep axi_clk always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE , 64 ,0x00000020, 5, 0,RW,"keep master_out_clk(APB/AHB master) always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_CORE_CLK_GATE_DISABLE , 64 ,0x00000010, 4, 0,RW,"keep core_clk always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_ABORT_MODE , 64 ,0x00000008, 3, 0,RW,"0:abort after finishing current cmdbuf command.1:abort immediately "), + VCMDREG(HWIF_VCMD_RESET_CORE , 64 ,0x00000004, 2, 0,RW,"sw write 1 to this bit will rset HW core logic when AXI/APB bus is idle."), + VCMDREG(HWIF_VCMD_RESET_ALL , 64 ,0x00000002, 1, 0,RW,"sw write 1 to this bit will rset HW immediately including all swregs and AXI/APB bus logic"), + VCMDREG(HWIF_VCMD_START_TRIGGER , 64 ,0x00000001, 0, 0,RW,"0:abort previou task and stop hw. 1:trigger hw to fetch and execute commands."), + VCMDREG(HWIF_VCMD_IRQ_INTCMD , 68 ,0xffff0000, 16, 0,RW,"interrupt sources which are triggered by command buffer id.. Only for version 1.0.c"), + VCMDREG(HWIF_VCMD_IRQ_JMPP , 68 ,0x00000080, 7, 0,RW,"interrupt source which is triggered by JMP command when hw goes to PEND state."), + VCMDREG(HWIF_VCMD_IRQ_JMPD , 68 ,0x00000040, 6, 0,RW,"interrupt source which is triggered by JMP command directly."), + VCMDREG(HWIF_VCMD_IRQ_RESET , 68 ,0x00000020, 5, 0,RW,"interrupt source which is triggered by hw reset or sw_vcmd_reset_all."), + VCMDREG(HWIF_VCMD_IRQ_ABORT , 68 ,0x00000010, 4, 0,RW,"interrupt source which is triggered by abort operation."), + VCMDREG(HWIF_VCMD_IRQ_CMDERR , 68 ,0x00000008, 3, 0,RW,"interrupt source which is triggered when there is illegal command in cmdbuf"), + VCMDREG(HWIF_VCMD_IRQ_TIMEOUT , 68 ,0x00000004, 2, 0,RW,"interrupt source which is triggered when vcmd timeout."), + VCMDREG(HWIF_VCMD_IRQ_BUSERR , 68 ,0x00000002, 1, 0,RW,"interrupt source which is triggered when there is bus error."), + VCMDREG(HWIF_VCMD_IRQ_ENDCMD , 68 ,0x00000001, 0, 0,RW,"interrupt source which is triggered by END command."), + VCMDREG(HWIF_VCMD_IRQ_INTCMD_EN , 72 ,0xffff0000, 16, 0,RW,"interrupt sources which are triggered by command buffer id. Only for version 1.0.c"), + VCMDREG(HWIF_VCMD_IRQ_JMPP_EN , 72 ,0x00000080, 7, 0,RW,"interrupt enable for sw_vcmd_irq_jmpp"), + VCMDREG(HWIF_VCMD_IRQ_JMPD_EN , 72 ,0x00000040, 6, 0,RW,"interrupt enable for sw_vcmd_irq_jmpd"), + VCMDREG(HWIF_VCMD_IRQ_RESET_EN , 72 ,0x00000020, 5, 0,RW,"interrupt enable for sw_vcmd_irq_reset"), + VCMDREG(HWIF_VCMD_IRQ_ABORT_EN , 72 ,0x00000010, 4, 0,RW,"interrupt enable for sw_vcmd_irq_abort"), + VCMDREG(HWIF_VCMD_IRQ_CMDERR_EN , 72 ,0x00000008, 3, 0,RW,"interrupt enable for sw_vcmd_irq_cmderr"), + VCMDREG(HWIF_VCMD_IRQ_TIMEOUT_EN , 72 ,0x00000004, 2, 0,RW,"interrupt enable for sw_vcmd_irq_timeout"), + VCMDREG(HWIF_VCMD_IRQ_BUSERR_EN , 72 ,0x00000002, 1, 0,RW,"interrupt enable for sw_vcmd_irq_buserr"), + VCMDREG(HWIF_VCMD_IRQ_ENDCMD_EN , 72 ,0x00000001, 0, 0,RW,"interrupt enable for sw_vcmd_irq_endcmd"), + VCMDREG(HWIF_VCMD_TIMEOUT_EN , 76 ,0x80000000, 31, 0,RW,"1:timeout work. 0: timeout do not work"), + VCMDREG(HWIF_VCMD_TIMEOUT_CYCLES , 76 ,0x7fffffff, 0, 0,RW,"sw_vcmd_irq_timeout will be generated when timeout counter is equal to this value."), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_ADDR , 80 ,0xffffffff, 0, 0,RW,"the least 32 bits address of the executing command"), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_ADDR_MSB , 84 ,0xffffffff, 0, 0,RW,"the most 32 bits address of the executing command"), + VCMDREG(HWIF_VCMD_EXE_CMDBUF_LENGTH , 88 ,0x0000ffff, 0, 0,RW,"the length of current command buffer in unit of 64bits."), + VCMDREG(HWIF_VCMD_CMD_SWAP , 92 ,0xf0000000, 28, 0,RW,"axi data swapping"), + VCMDREG(HWIF_VCMD_MAX_BURST_LEN , 92 ,0x00ff0000, 16, 0,RW,"max burst length which will be sent to axi bus"), + VCMDREG(HWIF_VCMD_AXI_ID_RD , 92 ,0x0000ff00, 8, 0,RW,"the arid which will be used on axi bus reading"), + VCMDREG(HWIF_VCMD_AXI_ID_WR , 92 ,0x000000ff, 0, 0,RW,"the awid which will be used on axi bus writing"), + VCMDREG(HWIF_VCMD_RDY_CMDBUF_COUNT , 96 ,0xffffffff, 0, 0,RW,"sw increases this counter by 1 after one more command buffer was ready."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU_GATE , 100,0x10000000, 28, 0,RW,"external abnormal interrupt source from mmu of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE_GATE, 100,0x08000000, 27, 0,RW,"external abnormal interrupt source from l2cache of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400_GATE, 100,0x04000000, 26, 0,RW,"external abnormal interrupt source from dec400 of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_GATE , 100,0x01000000, 24, 0,RW,"external abnormal interrupt source from vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU_GATE, 100,0x00200000, 21, 0,RW,"external abnormal interrupt source from mmu of cutree gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU_GATE , 100,0x00100000, 20, 0,RW,"external abnormal interrupt source from mmu of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE_GATE, 100,0x00080000, 19, 0,RW,"external abnormal interrupt source from l2 cache of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400_GATE, 100,0x00040000, 18, 0,RW,"external abnormal interrupt source from dec400 of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_GATE , 100,0x00020000, 17, 0,RW,"external abnormal interrupt source from cutree gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_GATE , 100,0x00010000, 16, 0,RW,"external abnormal interrupt source from vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU_GATE , 100,0x00000800, 11, 0,RW,"external normal interrupt source from mmu of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE_GATE, 100,0x00000400, 10, 0,RW,"external normal interrupt source from l2cache of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400_GATE, 100,0x00000200, 9, 0,RW,"external normal interrupt source from dec400 of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_GATE , 100,0x00000100, 8, 0,RW,"external normal interrupt source from vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU_GATE, 100,0x00000020, 5, 0,RW,"external normal interrupt source from mmu of cutree gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU_GATE , 100,0x00000010, 4, 0,RW,"external normal interrupt source from mmu of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE_GATE, 100,0x00000008, 3, 0,RW,"external normal interrupt source from l2 cache of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400_GATE, 100,0x00000004, 2, 0,RW,"external normal interrupt source from dec400 of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_GATE , 100,0x00000002, 1, 0,RW,"external normal interrupt source from cutree gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_GATE , 100,0x00000001, 0, 0,RW,"external normal interrupt source from vce gate."), + VCMDREG(HWIF_VCMD_CMDBUF_EXECUTING_ID , 104,0xffffffff, 0, 0,RW,"The ID of current executing command buffer.used after version 1.1.2."), diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.c b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.c new file mode 100644 index 000000000..c7e2d7673 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.c @@ -0,0 +1,148 @@ +/* + * Hantro Decoder device driver (kernel module) +* +* Copyright (C) 2020 VeriSilicon Microelectronics Co., 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, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------ + + Table of contents + + 1. Include headers + 2. External compiler flags + 3. Module defines + +------------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------------ + 1. Include headers +------------------------------------------------------------------------------*/ +#include /* for ioread32 and iowrite32 ... */ +#include "vcmdswhwregisters.h" + + +/* NOTE: Don't use ',' in descriptions, because it is used as separator in csv + * parsing. */ +const regVcmdField_s asicVcmdRegisterDesc[] = +{ +#include "vcmdregistertable.h" +}; + +/*------------------------------------------------------------------------------ + 2. External compiler flags +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- + 3. Module defines +------------------------------------------------------------------------------*/ + +/* Define this to print debug info for every register write. +#define DEBUG_PRINT_REGS */ + +/******************************************************************************* + Function name : vcmd_read_reg + Description : Retrive the content of a hadware register + Note: The status register will be read after every MB + so it may be needed to buffer it's content if reading + the HW register is slow. + Return type : u32 + Argument : u32 offset +*******************************************************************************/ +u32 vcmd_read_reg(const void *hwregs, u32 offset) +{ + u32 val; + + val =(u32) ioread32((void*)hwregs + offset); + + PDEBUG("vcmd_read_reg 0x%02x --> %08x\n", offset, val); + + return val; +} + +/******************************************************************************* + Function name : vcmd_write_reg + Description : Set the content of a hadware register + Return type : void + Argument : u32 offset + Argument : u32 val +*******************************************************************************/ +void vcmd_write_reg(const void *hwregs, u32 offset, u32 val) +{ + iowrite32(val,(void*)hwregs + offset); + + PDEBUG("vcmd_write_reg 0x%02x with value %08x\n", offset, val); +} + + +/*------------------------------------------------------------------------------ + + vcmd_write_register_value + + Write a value into a defined register field (write will happens actually). + +------------------------------------------------------------------------------*/ +void vcmd_write_register_value(const void *hwregs,u32* reg_mirror,regVcmdName name, u32 value) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + +#ifdef DEBUG_PRINT_REGS + PDEBUG("vcmd_write_register_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, value, field->description); +#endif + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("(field->mask >> field->lsb) >= value=%d\n",(field->mask >> field->lsb) >= value); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT*4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT*4); + + /* Clear previous value of field in register */ + regVal = reg_mirror[field->base/4] & ~(field->mask); + + /* Put new value of field in register */ + reg_mirror[field->base/4] = regVal | ((value << field->lsb) & field->mask); + + /* write it into HW registers */ + vcmd_write_reg(hwregs, field->base,reg_mirror[field->base/4]); +} + +/*------------------------------------------------------------------------------ + + vcmd_get_register_value + + Get an unsigned value from the ASIC registers + +------------------------------------------------------------------------------*/ +u32 vcmd_get_register_value(const void *hwregs, u32* reg_mirror,regVcmdName name) +{ + const regVcmdField_s *field; + u32 value; + + field = &asicVcmdRegisterDesc[name]; + + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + value = reg_mirror[field->base / 4] = vcmd_read_reg(hwregs, field->base); + value = (value & field->mask) >> field->lsb; + + return value; +} + + diff --git a/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.h b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.h new file mode 100644 index 000000000..59c2279cc --- /dev/null +++ b/drivers/staging/media/vpu-vc8000d-kernel/linux/subsys_driver/vcmdswhwregisters.h @@ -0,0 +1,233 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +/*------------------------------------------------------------------------------ + + Table of contents + + 1. Include headers + 2. External compiler flags + 3. Module defines + +------------------------------------------------------------------------------*/ +#ifndef VCMD_SWHWREGISTERS_H +#define VCMD_SWHWREGISTERS_H + +/*------------------------------------------------------------------------------ + 1. Include headers +------------------------------------------------------------------------------*/ +#ifdef __FREERTOS__ +#include "basetype.h" +#include "io_tools.h" +#elif defined(__linux__) +#ifndef MODEL_SIMULATION +#include +#include +#include +#endif +#endif + +#ifdef __FREERTOS__ +//addr_t has been defined in basetype.h //Now the FreeRTOS mem need to support 64bit env +#elif defined(__linux__) +typedef size_t addr_t; +#endif +typedef addr_t ptr_t; + +#undef PDEBUG /* undef it, just in case */ +#ifdef REGISTER_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "memalloc: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, ...) /* not debugging: nothing */ +#endif + +/*------------------------------------------------------------------------------ + 2. External compiler flags +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- + 3. Module defines +------------------------------------------------------------------------------*/ + +#define ASIC_VCMD_SWREG_AMOUNT 27 +#define VCMD_REGISTER_CONTROL_OFFSET 0X40 +#define VCMD_REGISTER_INT_STATUS_OFFSET 0X44 +#define VCMD_REGISTER_INT_CTL_OFFSET 0X48 +#define VCMD_REGISTER_EXT_INT_GATE_OFFSET 0X64 + +/* HW Register field names */ +typedef enum +{ +#include "vcmdregisterenum.h" + VcmdRegisterAmount +} regVcmdName; + +/* HW Register field descriptions */ +typedef struct +{ + u32 name; /* Register name and index */ + int base; /* Register base address */ + u32 mask; /* Bitmask for this field */ + int lsb; /* LSB for this field [31..0] */ + int trace; /* Enable/disable writing in swreg_params.trc */ + int rw; /* 1=Read-only 2=Write-only 3=Read-Write */ + char *description; /* Field description */ +} regVcmdField_s; + +/* Flags for read-only, write-only and read-write */ +#define RO 1 +#define WO 2 +#define RW 3 + +#define REGBASE(reg) (asicVcmdRegisterDesc[reg].base) + +/* Description field only needed for system model build. */ +#ifdef TEST_DATA +#define VCMDREG(name, base, mask, lsb, trace, rw, desc) \ + {name, base, mask, lsb, trace, rw, desc} +#else +#define VCMDREG(name, base, mask, lsb, trace, rw, desc) \ + {name, base, mask, lsb, trace, rw, ""} +#endif + + +/*------------------------------------------------------------------------------ + 4. Function prototypes +------------------------------------------------------------------------------*/ +extern const regVcmdField_s asicVcmdRegisterDesc[]; + +/*------------------------------------------------------------------------------ + + EncAsicSetRegisterValue + + Set a value into a defined register field + +------------------------------------------------------------------------------*/ +static inline void vcmd_set_register_mirror_value(u32 *reg_mirror, regVcmdName name, u32 value) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + +#ifdef DEBUG_PRINT_REGS + printf("vcmd_set_register_mirror_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, value, field->description); +#endif + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("(field->mask >> field->lsb) >= value=%d\n",(field->mask >> field->lsb) >= value); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + /* Clear previous value of field in register */ + regVal = reg_mirror[field->base / 4] & ~(field->mask); + + /* Put new value of field in register */ + reg_mirror[field->base / 4] = regVal | ((value << field->lsb) & field->mask); +} +static inline u32 vcmd_get_register_mirror_value(u32 *reg_mirror, regVcmdName name) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + regVal = reg_mirror[field->base / 4]; + regVal = (regVal & field->mask) >> field->lsb; + +#ifdef DEBUG_PRINT_REGS + PDEBUG("vcmd_get_register_mirror_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, regVal, field->description); +#endif + return regVal; +} + +u32 vcmd_read_reg(const void *hwregs, u32 offset); + +void vcmd_write_reg(const void *hwregs, u32 offset, u32 val); + + +void vcmd_write_register_value(const void *hwregs,u32* reg_mirror,regVcmdName name, u32 value); +u32 vcmd_get_register_value(const void *hwregs, u32* reg_mirror,regVcmdName name); + +#define vcmd_set_addr_register_value(reg_base, reg_mirror, name, value) do {\ + if(sizeof(addr_t) == 8) {\ + vcmd_write_register_value((reg_base), (reg_mirror),name, (u32)((addr_t)value)); \ + vcmd_write_register_value((reg_base), (reg_mirror),name##_MSB, (u32)(((addr_t)value) >> 32));\ + } else {\ + vcmd_write_register_value((reg_base),(reg_mirror), name, (u32)((addr_t)value));\ + }\ +}while (0) + +#define VCMDGetAddrRegisterValue(reg_base, reg_mirror,name) \ + ((sizeof(addr_t) == 8) ? (\ + (((addr_t)vcmd_get_register_value((reg_base),(reg_mirror), name)) | \ + (((addr_t)vcmd_get_register_value((reg_base), (reg_mirror),name##_MSB)) << 32))\ + ) : ((addr_t)vcmd_get_register_value((reg_base),(reg_mirror), (name)))) + +#endif /* VCMD_SWHWREGISTERS_H */ diff --git a/drivers/staging/media/vpu-vc8000e-kernel/COPYING b/drivers/staging/media/vpu-vc8000e-kernel/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/drivers/staging/media/vpu-vc8000e-kernel/Kconfig b/drivers/staging/media/vpu-vc8000e-kernel/Kconfig new file mode 100644 index 000000000..0ac10b0e9 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/Kconfig @@ -0,0 +1,3 @@ +config VIDEO_VC8000E + tristate "VC8000E support" + default m diff --git a/drivers/staging/media/vpu-vc8000e-kernel/Makefile b/drivers/staging/media/vpu-vc8000e-kernel/Makefile new file mode 100644 index 000000000..a0d8f2a47 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/Makefile @@ -0,0 +1,90 @@ +## + # Copyright (C) 2020 Alibaba Group Holding Limited +## +ifneq ($(wildcard ../.param),) + include ../.param +endif + +#CONFIG_DEBUG_MODE=1 +CONFIG_OUT_ENV=hwlinux + +CONFIG_BUILD_DRV_EXTRA_PARAM:="" + +DIR_TARGET_BASE=bsp/venc +DIR_TARGET_KO =bsp/venc/ko + +MODULE_NAME=VENC +BUILD_LOG_START="\033[47;30m>>> $(MODULE_NAME) $@ begin\033[0m" +BUILD_LOG_END ="\033[47;30m<<< $(MODULE_NAME) $@ end\033[0m" + +# +# Do a parallel build with multiple jobs, based on the number of CPUs online +# in this system: 'make -j8' on a 8-CPU system, etc. +# +# (To override it, run 'make JOBS=1' and similar.) +# + +ifeq ($(JOBS),) + JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) + ifeq ($(JOBS),) + JOBS := 1 + endif +endif + +all: info driver install_local_output +.PHONY: info driver install_local_output install_addons install_prepare clean_driver clean_output clean + +info: + @echo $(BUILD_LOG_START) + @echo " ====== Build Info from repo project ======" + @echo " BUILDROOT_DIR="$(BUILDROOT_DIR) + @echo " CROSS_COMPILE="$(CROSS_COMPILE) + @echo " LINUX_DIR="$(LINUX_DIR) + @echo " ARCH="$(ARCH) + @echo " BOARD_NAME="$(BOARD_NAME) + @echo " KERNEL_ID="$(KERNELVERSION) + @echo " KERNEL_DIR="$(LINUX_DIR) + @echo " INSTALL_DIR_ROOTFS="$(INSTALL_DIR_ROOTFS) + @echo " INSTALL_DIR_SDK="$(INSTALL_DIR_SDK) + @echo " ====== Build configuration by settings ======" +# @echo " CONFIG_DEBUG_MODE="$(CONFIG_DEBUG_MODE) + @echo " CONFIG_OUT_ENV="$(CONFIG_OUT_ENV) + @echo " JOBS="$(JOBS) + @echo $(BUILD_LOG_END) + +driver: + @echo $(BUILD_LOG_START) + make -C linux/kernel_module KDIR=$(LINUX_DIR) ARCH=$(ARCH) + @echo $(BUILD_LOG_END) + +clean_driver: + @echo $(BUILD_LOG_START) + make -C linux/kernel_module KDIR=$(LINUX_DIR) clean + @echo $(BUILD_LOG_END) + +install_prepare: + mkdir -p ./output/rootfs/$(DIR_TARGET_KO) + +install_addons: install_prepare + @if [ -d addons/ko ]; then \ + cp -rf addons/ko/* ./output/rootfs/$(DIR_TARGET_KO); \ + fi + +install_local_output: install_addons install_prepare driver + @echo $(BUILD_LOG_START) + find ./linux -name "*.ko" | xargs -i cp -f {} ./output/rootfs/$(DIR_TARGET_KO) + cp -f ./linux/kernel_module/driver_load.sh ./output/rootfs/$(DIR_TARGET_KO) + chmod +x ./output/rootfs/$(DIR_TARGET_KO)/*.sh + echo "vc8000" > ./output/rootfs/$(DIR_TARGET_KO)/vc8000e.conf + @if [ `command -v tree` != "" ]; then \ + tree ./output/rootfs; \ + fi + @echo $(BUILD_LOG_END) + +clean_output: + @echo $(BUILD_LOG_START) + rm -rf ./output + @echo $(BUILD_LOG_END) + +clean: clean_output clean_driver + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/README.md b/drivers/staging/media/vpu-vc8000e-kernel/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/insmod.sh b/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/insmod.sh new file mode 100755 index 000000000..cd6783296 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/insmod.sh @@ -0,0 +1,5 @@ +#!/bin/sh +KERNEL_VER=$(uname -r) +BASE_PATH=/lib/modules/${KERNEL_VER}/extra + +insmod $BASE_PATH/vc8000.ko diff --git a/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/rmmod.sh b/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/rmmod.sh new file mode 100755 index 000000000..7b55b5b0a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/addons/ko/rmmod.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rmmod vc8000 diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/Makefile b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/Makefile new file mode 100755 index 000000000..d7e638ca2 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/Makefile @@ -0,0 +1,204 @@ +############################################################################# +# +# The MIT License (MIT) +# +# Copyright (c) 2014 - 2021 VERISILICON +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +############################################################################# +# +# The GPL License (GPL) +# +# Copyright (C) 2014 - 2021 VERISILICON +# +# 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. +# +############################################################################# +# +# Note: This software is released under dual MIT and GPL licenses. A +# recipient may use this file under the terms of either the MIT license or +# GPL License. If you wish to use only one license not the other, you can +# indicate your decision by deleting one of the above license notices in your +# version of this file. +# +############################################################################## +ARM_CROSS_COMPILE ?= n + +ifeq ($(ARM_CROSS_COMPILE),y) +export ARCH=arm64 +export CROSS_COMPILE=/opt/kmb/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/aarch64-linux-gnu- +KDIR := /home/vsi/kmb-evm/kernel/mainline-tracking +endif + +SUPPORT_MMU = y +SUPPORT_AXIFE = n +SUPPORT_VCMD_ENABLE_IP = n + +ifeq ($(obj),) +obj = . +endif + +################################################# +# configuration + +MDIR := hantro + +# drivers objects +# list-multi := hantro_mmu.o + +# # what to build + +vc8000-objs := vc8000_driver.o vc8000_vcmd_driver.o bidirect_list.o vcmdswhwregisters.o vc8000_normal_driver.o +obj-m += vc8000.o + +ifeq ($(strip $(SUPPORT_MMU)),y) +vc8000-objs += hantro_mmu.o +endif + +ifeq ($(strip $(SUPPORT_AXIFE)),y) +vc8000-objs += vc8000_axife.o +endif + +tardest := . + +################################################# +# compile modules + +ifneq ($(KERNELRELEASE),) +# recursive call from kernel build system +dummy := $(shell echo $(KERNELRELEASE) > $(obj)/.version) + +ifeq ($(VERSION).$(PATCHLEVEL),2.6) + export-objs := + list-multi := +else + multi-m := $(filter $(list-multi), $(obj-m)) + int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + export-objs := $(filter $(int-m) $(obj-m),$(export-objs)) +endif + +CC += -I$(obj) +EXTRA_CFLAGS += -g + +# Print debugging messages from the device +#EXTRA_CFLAGS += -DHANTRO_DRIVER_DEBUG + +#EXTRA_CFLAGS += -DHANTROMMU_DEBUG +#EXTRA_CFLAGS += -DDYNAMIC_MALLOC_VCMDNODE + +snapshot := $(wildcard $(obj)/.snapshot) +ifneq ($(snapshot),) +SNAPSHOT_CFLAGS := -DSNAPSHOT='$(shell cat $(snapshot))' +EXTRA_CFLAGS += $(SNAPSHOT_CFLAGS) +endif + +ifeq ($(strip $(SUPPORT_MMU)),y) +EXTRA_CFLAGS += -DHANTROMMU_SUPPORT +endif + +ifeq ($(strip $(SUPPORT_AXIFE)),y) +EXTRA_CFLAGS += -DHANTROAXIFE_SUPPORT +endif + +ifeq ($(strip $(SUPPORT_VCMD_ENABLE_IP)),y) +EXTRA_CFLAGS += -DHANTROVCMD_ENABLE_IP_SUPPORT +endif + +-include $(TOPDIR)/Rules.make +else +# take version info from last module build if available +KERNELRELEASE := $(shell cat $(obj)/.version 2>/dev/null || uname -r) +endif +ifneq ($(ARM_CROSS_COMPILE),y) +KDIR_BASE := /afs/hantro.com/projects/Testing/Board_Version_Control + +#KDIR := $(KDIR_BASE)/Realview_EB/SW/Linux/v0_0/linux-2.6.19-arm2 +#KDIR := $(KDIR_BASE)/Realview_EB/SW/Linux/linux-2.6.21-arm1/v0_0/linux-2.6.21-arm1 +#KDIR := $(KDIR_BASE)/Realview_PB/PB926EJS/SW/Linux/linux-2.6.24-arm2-spnlck/v0_1/linux-2.6.24-arm2-spnlck +#KDIR := $(KDIR_BASE)/Realview_PB/PB926EJS/SW/Linux/linux-2.6.28-arm1/v0_1/linux-2.6.28-arm1 +#KDIR := $(KDIR_BASE)/SW_Common/ARM_realview_v6/2.6.28-arm1/v0_1-v6/linux-2.6.28-arm1 + +KVER := $(shell uname -r) +KDIR := /lib/modules/$(KVER)/build +endif + +PWD := $(shell pwd) +DEST := /lib/modules/$(KERNELRELEASE)/$(MDIR) + +# which files to install? +inst-m := $(wildcard *.ko) +ifeq ($(inst-m),) + inst-m := $(obj-m) +endif + +# locales seem to cause trouble sometimes. +LC_ALL = POSIX +export LC_ALL + +default:: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +install:: + strip --strip-debug $(inst-m) + -su -c "mkdir -p $(DEST); cp -v $(inst-m) $(DEST); depmod -a" + +clean:: + $(MAKE) -C $(KDIR) M=$(PWD) clean + -rm -f .version + +################################################# +# build tarballs + +thisdir := $(notdir $(PWD)) +name := $(shell echo $(thisdir) | sed 's/-.*//') +ver := $(shell echo $(thisdir) | sed 's/.*-//') +date := $(shell date +%Y%m%d) +tardest ?= . + +snapdir := $(HOME)/snapshot +snap ?= $(name) + +release: clean + rm -f .snapshot + (cd ..; tar cvzf $(tardest)/$(name)-$(ver).tar.gz $(thisdir)) + +snapshot snap tarball: clean + echo $(date) > .snapshot + (cd ..; tar czf $(snapdir)/$(snap)-$(date).tar.gz $(thisdir)) + $(MAKE) -C $(snapdir) + +################################################# +# other stuff + +%.asm: %.o + objdump -S $< > $@ + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/README b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/README new file mode 100755 index 000000000..23378984f --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/README @@ -0,0 +1,42 @@ + +-- BUILD -- + +You need a fully configured kernel source tree in order to build the +driver. Please set the location of the kernel tree in the Makefile (KDIR). +If you want some extra debug information in the kernel logs, you could +define the HANTRO_DRIVER_DEBUG but please be aware that allot of things are traced +with this option. +Also you could set a particular device MAJOR in the 'vc8000_normal_driver.c' and 'vc8000_vcmd_driver.c' +if you don't want dynamic allocation. + +Just run in this dir: + +%make + +If you want to install the modules please check first the install destination +in the Makefile (MDIR, DEST) and run: + +%make install + +-- USAGE -- + +Run script driver_load.sh to do all the things described below. +> sh driver_load.sh vcmd=0 + +First of all the module has to be inserted into the kernel with: +(you need a Linux shell cmd line) + +%insmod vc8000.ko vcmd_supported=1 + +Second of all a char device file has to be created: + +%mknod /dev/vc8000 c $MAJOR 0 + +Replace MAJOR = 254 with the correct value (i.e. read /proc/devices to find out +the exact value). + +Make sure that you have RW rights for the newly created dev file (use 'chmod'). + +The 'driver_load' script is provided for preparing all the things necessary for +the driver to be usable. The script is using 'cat' to retrieve the device's +major from /proc/devices. Remember to set the driver parameters. diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.c new file mode 100644 index 000000000..dc38ac25a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.c @@ -0,0 +1,222 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#ifdef __FREERTOS__ +#include +#include "osal.h" +#elif defined(__linux__) +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +/* request_irq(), free_irq() */ +#include +#include + +#include +#include +/* needed for virt_to_phys() */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#else //For other os +//TODO... +#endif +#include "bidirect_list.h" + +void init_bi_list(bi_list* list) +{ + list->head = NULL; + list->tail = NULL; +} + +bi_list_node* bi_list_create_node(void) +{ + bi_list_node* node=NULL; + node=(bi_list_node*)vmalloc(sizeof(bi_list_node)); + if(node==NULL) + { + PDEBUG ("%s\n","vmalloc for node fail!"); + return node; + } + memset(node,0,sizeof(bi_list_node)); + return node; +} +void bi_list_free_node(bi_list_node* node) +{ + //free current node + vfree(node); + return; +} + +void bi_list_insert_node_tail(bi_list* list,bi_list_node* current_node) +{ + if(current_node==NULL) + { + PDEBUG ("%s\n","insert node tail NULL"); + return; + } + if(list->tail) + { + current_node->previous=list->tail; + list->tail->next=current_node; + list->tail=current_node; + list->tail->next=NULL; + } + else + { + list->head=current_node; + list->tail=current_node; + current_node->next=NULL; + current_node->previous=NULL; + } + return; +} + +void bi_list_insert_node_before(bi_list* list,bi_list_node* base_node,bi_list_node* new_node) +{ + bi_list_node* temp_node_previous=NULL; + if(new_node==NULL) + { + PDEBUG ("%s\n","insert node before new node NULL"); + return; + } + if(base_node) + { + if(base_node->previous) + { + //at middle position + temp_node_previous = base_node->previous; + temp_node_previous->next=new_node; + new_node->next = base_node; + base_node->previous = new_node; + new_node->previous=temp_node_previous; + } + else + { + //at head + base_node->previous = new_node; + new_node->next = base_node; + list->head=new_node; + new_node->previous = NULL; + } + } + else + { + //at tail + bi_list_insert_node_tail(list,new_node); + } + return; +} + + +void bi_list_remove_node(bi_list* list,bi_list_node* current_node) +{ + bi_list_node* temp_node_previous=NULL; + bi_list_node* temp_node_next=NULL; + if(current_node==NULL) + { + PDEBUG ("%s\n","remove node NULL"); + return; + } + temp_node_next=current_node->next; + temp_node_previous=current_node->previous; + + if(temp_node_next==NULL && temp_node_previous==NULL ) + { + //there is only one node. + list->head=NULL; + list->tail=NULL; + } + else if(temp_node_next==NULL) + { + //at tail + list->tail=temp_node_previous; + temp_node_previous->next=NULL; + } + else if( temp_node_previous==NULL) + { + //at head + list->head=temp_node_next; + temp_node_next->previous=NULL; + } + else + { + //at middle position + temp_node_previous->next=temp_node_next; + temp_node_next->previous=temp_node_previous; + } + return; +} + + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.h new file mode 100644 index 000000000..002cc99aa --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/bidirect_list.h @@ -0,0 +1,116 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _BIDIRECT_LIST_H_ +#define _BIDIRECT_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __FREERTOS__ +#include "dev_common_freertos.h" /* needed for the _IOW etc stuff used later */ +#elif defined(__linux__) +#include /* needed for the _IOW etc stuff used later */ +#else //For other os +//TODO... +#endif + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef BIDIRECTION_LIST_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "hmp4e: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(__FILE__ ":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/***********************************************************************************************************************************************\ +* +\**********************************************************************************************************************************************/ +typedef struct bi_list_node{ + void* data; + struct bi_list_node* next; + struct bi_list_node* previous; +}bi_list_node; +typedef struct bi_list{ + bi_list_node* head; + bi_list_node* tail; +}bi_list; + +void init_bi_list(bi_list* list); + +bi_list_node* bi_list_create_node(void); + +void bi_list_free_node(bi_list_node* node); + +void bi_list_insert_node_tail(bi_list* list,bi_list_node* current_node); + +void bi_list_insert_node_before(bi_list* list,bi_list_node* base_node,bi_list_node* new_node); + +void bi_list_remove_node(bi_list* list,bi_list_node* current_node); + +#ifdef __cplusplus +} +#endif + +#endif /* !_BIDIRECT_LIST_H_ */ diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/driver_load.sh b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/driver_load.sh new file mode 100755 index 000000000..438243f7b --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/driver_load.sh @@ -0,0 +1,61 @@ +#!/bin/sh +#dmesg -C +module="vc8000" +device="/dev/vc8000" +mode="666" +#Used to setup default parameters +DefaultParameter(){ + vcmd=1 + #default value can be added to here +} +echo + +if [ ! -e /dev ] +then + mkdir /dev/ +fi +echo "Help information:" +echo "Input format should be like as below" +echo "./driver_load.sh vcmd=0(default) or (1)" +if [ $# -eq 0 ] +then + DefaultParameter + echo " Default vcmd_supported value = $vcmd" +else + para_1="$1" + vcmd_input=${para_1##*=} + vcmd=$vcmd_input + if [ $vcmd -ne 0 ] && [ $vcmd -ne 1 ] + then + echo "Invalid vcmd_supported value, which = $vcmd" + echo "vcmd_supported should be 0 or 1" + fi + echo "vcmd_supported = $vcmd" +fi +#vcmd_supported = 0(default) or 1 +#insert module +insmod $module.ko vcmd_supported=$vcmd || exit 1 +#insmod $module.ko vcmd_supported=1 || exit 1 + +echo "module $module inserted" + +#remove old nod +rm -f $device + +#read the major asigned at loading time +major=`cat /proc/devices | grep $module | cut -c1-3` + +echo "$module major = $major" + +#create dev node +mknod $device c $major 0 + +echo "node $device created" + +#give all 'rw' access +chmod $mode $device + +echo "set node access to $mode" + +#the end +echo diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantro_mmu.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantro_mmu.c new file mode 100755 index 000000000..3df60e462 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantro_mmu.c @@ -0,0 +1,1897 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#ifdef __FREERTOS__ +#include "base_type.h" +#include "dev_common_freertos.h" +#include "io_tools.h" +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)) +#include +#else +#include +#endif +#include +#include +#include +#include +#endif + +#include "hantrommu.h" + +MODULE_DESCRIPTION("Verisilicon VPU Driver"); +MODULE_LICENSE("GPL"); + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +/******************************************************************************* +***** New MMU Defination *******************************************************/ +#define MMU_MTLB_SHIFT 22 +#define MMU_STLB_4K_SHIFT 12 +#define MMU_STLB_64K_SHIFT 16 + +#define MMU_MTLB_BITS (32 - MMU_MTLB_SHIFT) +#define MMU_PAGE_4K_BITS MMU_STLB_4K_SHIFT +#define MMU_STLB_4K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS) +#define MMU_PAGE_64K_BITS MMU_STLB_64K_SHIFT +#define MMU_STLB_64K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_64K_BITS) + +#define MMU_MTLB_ENTRY_NUM (1 << MMU_MTLB_BITS) +#define MMU_MTLB_SIZE (MMU_MTLB_ENTRY_NUM << 2) +#define MMU_STLB_4K_ENTRY_NUM (1 << MMU_STLB_4K_BITS) +#define MMU_STLB_4K_SIZE (MMU_STLB_4K_ENTRY_NUM << 2) +#define MMU_PAGE_4K_SIZE (1 << MMU_STLB_4K_SHIFT) +#define MMU_STLB_64K_ENTRY_NUM (1 << MMU_STLB_64K_BITS) +#define MMU_STLB_64K_SIZE (MMU_STLB_64K_ENTRY_NUM << 2) +#define MMU_PAGE_64K_SIZE (1 << MMU_STLB_64K_SHIFT) + +#define MMU_MTLB_MASK (~((1U << MMU_MTLB_SHIFT)-1)) +#define MMU_STLB_4K_MASK ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK) +#define MMU_PAGE_4K_MASK (MMU_PAGE_4K_SIZE - 1) +#define MMU_STLB_64K_MASK ((~((1U << MMU_STLB_64K_SHIFT)-1)) ^ MMU_MTLB_MASK) +#define MMU_PAGE_64K_MASK (MMU_PAGE_64K_SIZE - 1) + +/* Page offset definitions. */ +#define MMU_OFFSET_4K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS) +#define MMU_OFFSET_4K_MASK ((1U << MMU_OFFSET_4K_BITS) - 1) +#define MMU_OFFSET_16K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_16K_BITS) +#define MMU_OFFSET_16K_MASK ((1U << MMU_OFFSET_16K_BITS) - 1) + +#define MMU_MTLB_ENTRY_HINTS_BITS 6 +#define MMU_MTLB_ENTRY_STLB_MASK (~((1U << MMU_MTLB_ENTRY_HINTS_BITS) - 1)) + +#define MMU_MTLB_PRESENT 0x00000001 +#define MMU_MTLB_EXCEPTION 0x00000002 +#define MMU_MTLB_4K_PAGE 0x00000000 + +#define MMU_STLB_PRESENT 0x00000001 +#define MMU_STLB_EXCEPTION 0x00000002 +#define MMU_STLB_4K_PAGE 0x00000000 + +#define MMU_FALSE 0 +#define MMU_TRUE 1 + +#define MMU_ERR_OS_FAIL (0xffff) +#define MMU_EFAULT MMU_ERR_OS_FAIL +#define MMU_ENOTTY MMU_ERR_OS_FAIL + +#define MMU_INFINITE ((u32) ~0U) + +#define MAX_NOPAGED_SIZE 0x20000 +#define MMU_SUPPRESS_OOM_MESSAGE 1 + +#if MMU_SUPPRESS_OOM_MESSAGE +#define MMU_NOWARN __GFP_NOWARN +#else +#define MMU_NOWARN 0 +#endif + +#define MMU_IS_ERROR(status) (status < 0) +#define MMU_NO_ERROR(status) (status >= 0) +#define MMU_IS_SUCCESS(status) (status == MMU_STATUS_OK) + +#undef MMUDEBUG +#ifdef HANTROMMU_DEBUG +# ifdef __KERNEL__ +# define MMUDEBUG(fmt, args...) printk( KERN_INFO "hantrommu: " fmt, ## args) +# else +# define MMUDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define MMUDEBUG(fmt, args...) +#endif + +#define MMU_ON_ERROR(func) \ + do { \ + status = func; \ + if (MMU_IS_ERROR(status)){ \ + goto onerror; \ + } \ + }while (MMU_FALSE) + +#define WritePageEntry(page_entry, entry_value) \ + *(unsigned int *)(page_entry) =(unsigned int)(entry_value) + +#define ReadPageEntry(page_entry) *(unsigned int *)(page_entry) + +#define DRIVER_NAME "hantroencdma" + +/* simple map mode: generate mmu address which is same as input bus address*/ +unsigned int simple_map = 0; +/* this shift should be an integral multiple of mmu page size(4096). + It can generate a mmu address shift in simple map mode*/ +unsigned int map_shift = 0; + +/* module_param(name, type, perm) */ +module_param(simple_map, uint, 0); +module_param(map_shift, uint, 0); + +enum MMURegion { + MMU_REGION_IN, + MMU_REGION_OUT, + MMU_REGION_PRIVATE, + MMU_REGION_PUB, + + MMU_REGION_COUNT +}; + +struct MMUNode { + void *buf_virtual_address; + unsigned int buf_bus_address; /* used in kernel map mode */ + int mtlb_start; + int stlb_start; + int mtlb_end; + int stlb_end; + unsigned int page_count; + int process_id; + struct file* filp; + + struct MMUNode *next; + struct MMUNode *prev; +}; + +struct MMUDDRRegion { + unsigned long long physical_address; + unsigned long long virtual_address; + unsigned int page_count; + + void *node_mutex; + struct MMUNode *simple_map_head; + struct MMUNode *simple_map_tail; + struct MMUNode *free_map_head; + struct MMUNode *map_head; + struct MMUNode *free_map_tail; + struct MMUNode *map_tail; +}; + +struct MMU { + void *page_table_mutex; + /* Master TLB information. */ + unsigned int mtlb_size; + unsigned long long mtlb_physical; + void *mtlb_virtual; + unsigned int mtlb_entries; + + int enabled; + unsigned int stlb_size; + unsigned long long stlb_physical; + void *stlb_virtual; + struct MMUDDRRegion region[MMU_REGION_COUNT]; + unsigned int page_table_array_size; + unsigned long long page_table_array_physical; + void *page_table_array; +}; + +static struct MMU *g_mmu = NULL; +extern unsigned long gBaseDDRHw; +unsigned int mmu_enable = MMU_FALSE; +static unsigned int mmu_init = MMU_FALSE; +extern unsigned int pcie; +static unsigned int region_in_mmu_start = REGION_IN_MMU_START; +static unsigned int region_in_mmu_end = REGION_IN_MMU_END; +static unsigned int region_out_mmu_start = REGION_OUT_MMU_START; +static unsigned int region_out_mmu_end = REGION_OUT_MMU_END; +static unsigned int region_private_mmu_start = REGION_PRIVATE_MMU_START; +static unsigned int region_private_mmu_end = REGION_PRIVATE_MMU_END; + +static const struct platform_device_info hantro_platform_info = { + .name = DRIVER_NAME, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int hantro_drm_probe(struct platform_device *pdev) +{ + int result; + struct device *dev = &pdev->dev; + (void) dev; + (void) result; + return 0; +} +static int hantro_drm_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + (void) dev; + return 0; +} +static const struct platform_device_id hantro_drm_platform_ids[] = { + { + .name = DRIVER_NAME, + }, + {/* sentinel */ }, +}; +static const struct of_device_id hantro_of_match[] = { + { .compatible = "thead,light-vc8000e-mmu", }, + {/* sentinel */} +}; +static struct platform_driver hantro_drm_platform_driver = { + .probe = hantro_drm_probe, + .remove = hantro_drm_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = hantro_of_match, + }, + .id_table = hantro_drm_platform_ids, +}; + +struct platform_device *platformdev; + +static enum MMUStatus ZeroMemory(void *memory, unsigned int bytes) { + memset(memory, 0, bytes); + + return MMU_STATUS_OK; +} + +static enum MMUStatus AllocateMemory(unsigned int bytes, void **memory){ + void *pointer; + enum MMUStatus status; + + if (bytes > MAX_NOPAGED_SIZE) { + pointer = (void*) vmalloc(bytes); + MMUDEBUG(" *****VMALLOC size*****%d\n", bytes); + } else { + pointer = (void*) kmalloc(bytes, GFP_KERNEL | MMU_NOWARN); + MMUDEBUG(" *****KMALLOC size*****%d\n", bytes); + } + + if (pointer == NULL) { + /* Out of memory. */ + status = MMU_STATUS_OUT_OF_MEMORY; + goto onerror; + } + + /* Return pointer to the memory allocation. */ + *memory = pointer; + + return MMU_STATUS_OK; + +onerror: + /* Return the status. */ + return status; +} + +static enum MMUStatus FreeMemory(void *memory) { + /* Free the memory from the OS pool. */ + if (is_vmalloc_addr(memory)) { + MMUDEBUG(" *****VFREE*****%p\n", memory); + vfree(memory); + } else { + MMUDEBUG(" *****KFREE*****%p\n", memory); + kfree(memory); + } + return MMU_STATUS_OK; +} + + +static enum MMUStatus SMDeleteNode(struct MMUNode **pp) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + + MMUDEBUG(" *****DeleteNode size*****%d\n", (*pp)->page_count); + FreeMemory(*pp); + + return MMU_STATUS_OK; +} + +static enum MMUStatus DeleteNode(struct MMUNode **pp) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + + MMUDEBUG(" *****DeleteNode size*****%d\n", (*pp)->page_count); + FreeMemory(*pp); + + return MMU_STATUS_OK; +} + +static enum MMUStatus MergeNode(struct MMUNode *h, + struct MMUNode **pp) { + struct MMUNode *tmp0 = h->next; + struct MMUNode *tmp1 = h->next; + while(tmp0) { + /* 1th step: find front contiguous memory node */ + if(tmp0->mtlb_end == (*pp)->mtlb_start && + tmp0->stlb_end == (*pp)->stlb_start) { + tmp0->mtlb_end = (*pp)->mtlb_end; + tmp0->stlb_end = (*pp)->stlb_end; + tmp0->page_count += (*pp)->page_count; + DeleteNode(pp); + MMUDEBUG(" *****first merge to front. node size*****%d\n", tmp0->page_count); + /* after merge to front contiguous memory node, + find if there is behind contiguous memory node */ + while(tmp1) { + /* merge */ + if(tmp1->mtlb_start == tmp0->mtlb_end && + tmp1->stlb_start == tmp0->stlb_end) { + tmp1->mtlb_start = tmp0->mtlb_start; + tmp1->stlb_start = tmp0->stlb_start; + tmp1->page_count += tmp0->page_count; + MMUDEBUG(" *****second merge to behind. node size*****%d\n", tmp1->page_count); + DeleteNode(&tmp0); + return MMU_STATUS_OK; + } + tmp1 = tmp1->next; + } + return MMU_STATUS_OK; + /* 1th step: find behind contiguous memory node */ + } else if(tmp0->mtlb_start == (*pp)->mtlb_end && + tmp0->stlb_start == (*pp)->stlb_end) { + tmp0->mtlb_start = (*pp)->mtlb_start; + tmp0->stlb_start = (*pp)->stlb_start; + tmp0->page_count += (*pp)->page_count; + DeleteNode(pp); + MMUDEBUG(" *****first merge to behind. node size*****%d\n", tmp0->page_count); + /* after merge to behind contiguous memory node, + find if there is front contiguous memory node */ + while(tmp1) { + /* merge */ + if(tmp1->mtlb_end == tmp0->mtlb_start && + tmp1->stlb_end == tmp0->stlb_start) { + tmp1->mtlb_end = tmp0->mtlb_end; + tmp1->stlb_end = tmp0->stlb_end; + tmp1->page_count += tmp0->page_count; + MMUDEBUG(" *****second merge to front. node size*****%d\n", tmp1->page_count); + DeleteNode(&tmp0); + return MMU_STATUS_OK; + } + tmp1 = tmp1->next; + } + return MMU_STATUS_OK; + } + tmp0 = tmp0->next; + } + return MMU_STATUS_FALSE; +} + +/* Insert a node to map list */ +static enum MMUStatus SMInsertNode(enum MMURegion e, + struct MMUNode **pp) { + struct MMUNode *h; + + h = g_mmu->region[e].simple_map_head; + + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + MMUDEBUG(" *****insert bm node*****%d\n", (*pp)->page_count); + + return MMU_STATUS_OK; +} + +static enum MMUStatus InsertNode(enum MMURegion e, + struct MMUNode **pp, + unsigned int free) { + enum MMUStatus status; + struct MMUNode *h, *b; + + if(free) { + h = g_mmu->region[e].free_map_head; + b = g_mmu->region[e].map_head; + status = MergeNode(h, pp); + MMUDEBUG(" *****insert free*****%d\n", (*pp)->page_count); + if(MMU_IS_ERROR(status)) { + /* remove from map*/ + if((*pp)->prev != NULL && (*pp)->next != NULL) { + (*pp)->prev->next = (*pp)->next; + (*pp)->next->prev = (*pp)->prev; + } + /* insert to free map */ + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + } + } else { + h = g_mmu->region[e].map_head; + + h->next->prev = *pp; + (*pp)->next = h->next; + (*pp)->prev = h; + h->next = *pp; + MMUDEBUG(" *****insert unfree*****%d\n", (*pp)->page_count); + } + + return MMU_STATUS_OK; +} + +/* Create a Node */ +static enum MMUStatus SMCreateNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p, **new; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + new = &p; + + (*new)->mtlb_start = -1; + (*new)->stlb_start = -1; + (*new)->mtlb_end = -1; + (*new)->stlb_end = -1; + (*new)->process_id = 0; + (*new)->filp = NULL; + (*new)->page_count = 0; + (*new)->prev = NULL; + (*new)->next = NULL; + /* Insert a uncomplete Node, it will be initialized later */ + SMInsertNode(e, new); + + /* return a new node for map buffer */ + *node = *new; + return MMU_STATUS_OK; +} + +/* Create initial Nodes */ +static enum MMUStatus SMCreateNodes(void) { + struct MMUNode *simple_map_head; + struct MMUNode *simple_map_tail; + int i; + + /* Init each region map node */ + for (i = 0; i < MMU_REGION_COUNT ; i++) { + simple_map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + simple_map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + + simple_map_head->mtlb_start = -1; + simple_map_head->stlb_start = -1; + simple_map_head->mtlb_end = -1; + simple_map_head->stlb_end = -1; + simple_map_head->process_id = 0; + simple_map_head->filp = NULL; + simple_map_head->page_count = 0; + simple_map_head->prev = NULL; + simple_map_head->next = simple_map_tail; + + simple_map_tail->mtlb_start = -1; + simple_map_tail->stlb_start = -1; + simple_map_tail->mtlb_end = -1; + simple_map_tail->stlb_end = -1; + simple_map_tail->process_id = 0; + simple_map_tail->filp = NULL; + simple_map_tail->page_count = 0; + simple_map_tail->prev = simple_map_head; + simple_map_tail->next = NULL; + + g_mmu->region[i].simple_map_head = simple_map_head; + g_mmu->region[i].simple_map_tail = simple_map_tail; + } + return MMU_STATUS_OK; +} + +static enum MMUStatus CreateNode(void) { + struct MMUNode *free_map_head, *map_head, *p, **pp; + struct MMUNode *free_map_tail, *map_tail; + int i; + unsigned int page_count; + unsigned int prev_stlb = 0, prev_mtlb = 0; + + /* Init each region map node */ + for (i = 0; i < MMU_REGION_COUNT ; i++) { + free_map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + map_head = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + free_map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + map_tail = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + + free_map_head->mtlb_start = map_head->mtlb_start = -1; + free_map_head->stlb_start = map_head->stlb_start = -1; + free_map_head->mtlb_end = map_head->mtlb_end = -1; + free_map_head->stlb_end = map_head->stlb_end = -1; + free_map_head->process_id = map_head->process_id = 0; + free_map_head->filp = map_head->filp = NULL; + free_map_head->page_count = map_head->page_count = 0; + free_map_head->prev = map_head->prev = NULL; + free_map_head->next = free_map_tail; + map_head->next = map_tail; + + free_map_tail->mtlb_start = map_tail->mtlb_start = -1; + free_map_tail->stlb_start = map_tail->stlb_start = -1; + free_map_tail->mtlb_end = map_tail->mtlb_end = -1; + free_map_tail->stlb_end = map_tail->stlb_end = -1; + free_map_tail->process_id = map_tail->process_id = 0; + free_map_tail->filp = map_tail->filp = NULL; + free_map_tail->page_count = map_tail->page_count = 0; + free_map_tail->prev = free_map_head; + map_tail->prev = map_head; + free_map_tail->next = map_tail->next = NULL; + + g_mmu->region[i].free_map_head = free_map_head; + g_mmu->region[i].map_head = map_head; + g_mmu->region[i].free_map_tail = free_map_tail; + g_mmu->region[i].map_tail = map_tail; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + pp = &p; + + switch(i) { + case MMU_REGION_IN: + page_count = (REGION_IN_END - REGION_IN_START + 1)/PAGE_SIZE; + p->stlb_start = region_in_mmu_start >> 12 & 0x3FF; //hold mmu addr: 0x0 + p->mtlb_start = region_in_mmu_start >> 22; + //end point next region start: +1; for remainder: +1 + p->stlb_end = prev_stlb = region_in_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_in_mmu_end >> 22; + p->page_count = page_count - 1; //hold mmu addr: 0x0 + break; + case MMU_REGION_OUT: + page_count = (REGION_OUT_END - REGION_OUT_START + 1)/PAGE_SIZE; + p->stlb_start = region_out_mmu_start >> 12 & 0x3FF; + p->mtlb_start = region_out_mmu_start >> 22; + p->stlb_end = prev_stlb = region_out_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_out_mmu_end >> 22; + p->page_count = page_count; + break; + case MMU_REGION_PRIVATE: + page_count = (REGION_PRIVATE_END - REGION_PRIVATE_START + 1)/PAGE_SIZE; + p->stlb_start = region_private_mmu_start >> 12 & 0x3FF; + p->mtlb_start = region_private_mmu_start >> 22; + p->stlb_end = prev_stlb = region_private_mmu_end >> 12 & 0x3FF; + p->mtlb_end = prev_mtlb = region_private_mmu_end >> 22; + p->page_count = page_count; + break; + case MMU_REGION_PUB: + p->stlb_start = prev_stlb; + p->mtlb_start = prev_mtlb; + p->stlb_end = prev_stlb = MMU_STLB_4K_ENTRY_NUM - 1; + p->mtlb_end = prev_mtlb = MMU_MTLB_ENTRY_NUM - 1; + p->page_count = (p->mtlb_end - p->mtlb_start) * MMU_STLB_4K_ENTRY_NUM + + p->stlb_end - p->stlb_start + 1; + break; + default: + pr_notice(" *****MMU Region Error*****\n"); + break; + } + + p->process_id = 0; + p->filp = NULL; + p->next = p->prev = NULL; + + InsertNode(i, pp, 1); + } + + return MMU_STATUS_OK; +} + +/* A simpile function to check if the map buffer is existed. + it needs more complex version*/ +static enum MMUStatus SMCheckAddress(enum MMURegion e, + void *virtual_address) { + struct MMUNode *p; + p = g_mmu->region[e].simple_map_head->next; + + while(p) { + if(p->buf_virtual_address == virtual_address) { + return MMU_STATUS_FALSE; + } + p = p->next; + } + return MMU_STATUS_OK; +} + +static enum MMUStatus FindFreeNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p; + p = g_mmu->region[e].free_map_head->next; + + while(p) { + if(p->page_count >= page_count) { + *node = p; + return MMU_STATUS_OK; + } + p = p->next; + } + return MMU_STATUS_FALSE; +} + +static enum MMUStatus SplitFreeNode(enum MMURegion e, + struct MMUNode **node, + unsigned int page_count) { + struct MMUNode *p, **new; + + p = kmalloc(sizeof(struct MMUNode), GFP_KERNEL | MMU_NOWARN); + new = &p; + + **new = **node; + + (*new)->mtlb_start = (*node)->mtlb_start; + (*new)->stlb_start = (*node)->stlb_start; + (*new)->mtlb_end = (page_count + (*node)->stlb_start) / + MMU_STLB_4K_ENTRY_NUM + + (*node)->mtlb_start; + (*new)->stlb_end = (page_count + (*node)->stlb_start) % + MMU_STLB_4K_ENTRY_NUM; + (*new)->process_id = (*node)->process_id; + (*new)->page_count = page_count; + + MMUDEBUG(" *****new mtlb_start*****%d\n", (*new)->mtlb_start); + MMUDEBUG(" *****new stlb_start*****%d\n", (*new)->stlb_start); + MMUDEBUG(" *****new mtlb_end*****%d\n", (*new)->mtlb_end); + MMUDEBUG(" *****new stlb_end*****%d\n", (*new)->stlb_end); + /* Insert a new node in map */ + InsertNode(e, new, 0); + + /* Update free node in free map*/ + (*node)->page_count -= page_count; + if((*node)->page_count == 0) { + DeleteNode(node); + MMUDEBUG(" *****old node deleted*****\n"); + } else { + (*node)->mtlb_start = (*new)->mtlb_end; + (*node)->stlb_start = (*new)->stlb_end; + + MMUDEBUG(" *****old mtlb_start*****%d\n", (*node)->mtlb_start); + MMUDEBUG(" *****old stlb_start*****%d\n", (*node)->stlb_start); + MMUDEBUG(" *****old mtlb_end*****%d\n", (*node)->mtlb_end); + MMUDEBUG(" *****old stlb_end*****%d\n", (*node)->stlb_end); + } + /* return a new node for map buffer */ + *node = *new; + + return MMU_STATUS_OK; +} + +static enum MMUStatus SMRemoveNode(enum MMURegion e, + void *buf_virtual_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].simple_map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_virtual_address == buf_virtual_address && + (*pp)->process_id == process_id) { + SMDeleteNode(pp); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus RemoveNode(enum MMURegion e, + void *buf_virtual_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_virtual_address == buf_virtual_address && + (*pp)->process_id == process_id) { + InsertNode(e, pp, 1); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus SMRemoveKernelNode(enum MMURegion e, + unsigned int buf_bus_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].simple_map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_bus_address == buf_bus_address && + (*pp)->process_id == process_id) { + SMDeleteNode(pp); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus RemoveKernelNode(enum MMURegion e, + unsigned int buf_bus_address, + unsigned int process_id) { + struct MMUNode *p, **pp; + p = g_mmu->region[e].map_head->next; + pp = &p; + + while(*pp) { + if((*pp)->buf_bus_address == buf_bus_address && + (*pp)->process_id == process_id) { + InsertNode(e, pp, 1); + break; + } + *pp = (*pp)->next; + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus Delay(unsigned int delay) { + if(delay > 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) + ktime_t dl = ktime_set((delay / MSEC_PER_SEC), + (delay % MSEC_PER_SEC) * NSEC_PER_MSEC); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&dl, HRTIMER_MODE_REL); +#else + msleep(delay); +#endif + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus CreateMutex(void **mtx) { + enum MMUStatus status; + + /* Allocate the mutex structure. */ + status = AllocateMemory(sizeof(struct mutex), mtx); + if (MMU_IS_SUCCESS(status)) { + /* Initialize the mutex. */ + mutex_init(*(struct mutex **)mtx); + } + + return status; +} + +static enum MMUStatus DeleteMutex(void *mtx) { + /* Destroy the mutex. */ + mutex_destroy((struct mutex *)mtx); + + /* Free the mutex structure. */ + FreeMemory(mtx); + + return MMU_STATUS_OK; +} + +static enum MMUStatus AcquireMutex(void *mtx, unsigned int timeout) { + if (timeout == MMU_INFINITE) + { + /* Lock the mutex. */ + mutex_lock(mtx); + + /* Success. */ + return MMU_STATUS_OK; + } + + for (;;) { + /* Try to acquire the mutex. */ + if (mutex_trylock(mtx)) { + /* Success. */ + return MMU_STATUS_OK; + } + + if (timeout-- == 0) { + break; + } + + /* Wait for 1 millisecond. */ + Delay(1); + } + + return MMU_STATUS_OK; +} + +static enum MMUStatus ReleaseMutex(void *mtx) { + /* Release the mutex. */ + mutex_unlock(mtx); + + return MMU_STATUS_OK; +} + + +static inline enum MMUStatus QueryProcessPageTable(void *logical, + unsigned long long *address) { + unsigned long lg = (unsigned long)logical; + unsigned long offset = lg & ~PAGE_MASK; + struct vm_area_struct *vma; + spinlock_t *ptl; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (is_vmalloc_addr(logical)) { + /* vmalloc area. */ + *address = page_to_phys(vmalloc_to_page(logical)) | offset; + return MMU_STATUS_OK; + } else if (virt_addr_valid(lg)) { + /* Kernel logical address. */ + *address = virt_to_phys(logical); + return MMU_STATUS_OK; + } else { + /* Try user VM area. */ + if (!current->mm) + return MMU_STATUS_NOT_FOUND; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + down_read(¤t->mm->mmap_lock); +#else + down_read(¤t->mm->mmap_sem); +#endif + vma = find_vma(current->mm, lg); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + up_read(¤t->mm->mmap_lock); +#else + up_read(¤t->mm->mmap_sem); +#endif + + /* To check if mapped to user. */ + if (!vma) + return MMU_STATUS_NOT_FOUND; + + pgd = pgd_offset(current->mm, lg); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + return MMU_STATUS_NOT_FOUND; + +#if (defined(CONFIG_CPU_CSKYV2) || defined(CONFIG_X86)) \ + && LINUX_VERSION_CODE >= KERNEL_VERSION (4,12,0) + pud = pud_offset((p4d_t*)pgd, lg); +#elif (defined(CONFIG_CPU_CSKYV2)) \ + && LINUX_VERSION_CODE >= KERNEL_VERSION (4,11,0) + pud = pud_offset((p4d_t*)pgd, lg); +#else + pud = pud_offset((p4d_t*)pgd, lg); +#endif + if (pud_none(*pud) || pud_bad(*pud)) + return MMU_STATUS_NOT_FOUND; + + pmd = pmd_offset(pud, lg); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + return MMU_STATUS_NOT_FOUND; + + pte = pte_offset_map_lock(current->mm, pmd, lg, &ptl); + if (!pte) { + spin_unlock(ptl); + return MMU_STATUS_NOT_FOUND; + } + + if (!pte_present(*pte)) { + pte_unmap_unlock(pte, ptl); + return MMU_STATUS_NOT_FOUND; + } + + *address = (pte_pfn(*pte) << PAGE_SHIFT) | offset; + pte_unmap_unlock(pte, ptl); + + *address -= gBaseDDRHw; + //MMUDEBUG(" QueryProcessPageTable map: virt %p -> %p\n", logical, (void *)*address); + + return MMU_STATUS_OK; + } +} + +static inline int GetProcessID(void) { + return current->tgid; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +static inline int is_vmalloc_addr(void *addr) { + unsigned long long addr = (unsigned long long)Addr; + + return addr >= VMALLOC_START && addr < VMALLOC_END; +} +#endif + +static enum MMUStatus GetPhysicalAddress(void *logical, + unsigned long long *address) { + enum MMUStatus status; + + status = QueryProcessPageTable(logical, address); + + return status; +} + +static enum MMUStatus GetPageEntry(struct MMUNode *node, + unsigned int **page_table_entry, + unsigned int i) { + int num = node->mtlb_start * MMU_STLB_4K_ENTRY_NUM + + node->stlb_start + i; + *page_table_entry = (unsigned int*)g_mmu->stlb_virtual + num; + return MMU_STATUS_OK; +} + +static enum MMUStatus SetupDynamicSpace(void) { + int i; + enum MMUStatus status; + unsigned int stlb_entry; + void *pointer; + unsigned long long address; + dma_addr_t dma_handle; + unsigned int num_entries = MMU_MTLB_ENTRY_NUM; + unsigned int *mtlb_virtual = (unsigned int *)g_mmu->mtlb_virtual; + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + if(pcie) { + pointer = ioremap(gBaseDDRHw+STLB_PCIE_START_ADDRESS, num_entries*MMU_STLB_4K_SIZE); + g_mmu->stlb_virtual = pointer; + MMUDEBUG(" *****stlb_virtual = %p**%d\n", pointer, num_entries*MMU_STLB_4K_SIZE); + address = STLB_PCIE_START_ADDRESS; + for(i = 0; i < num_entries; i++){ + stlb_entry = address + /* 4KB page size */ + | (0 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(mtlb_virtual++, stlb_entry); + address += MMU_STLB_4K_SIZE; + } + + } else { + g_mmu->stlb_virtual = (void *)((u64)(g_mmu->mtlb_virtual) + MMU_MTLB_SIZE); + g_mmu->stlb_physical = address = g_mmu->mtlb_physical + MMU_MTLB_SIZE; + g_mmu->stlb_size = num_entries * MMU_STLB_4K_SIZE; + + for(i = 0; i < num_entries; i++){ + stlb_entry = address + /* 4KB page size */ + | (0 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(mtlb_virtual++, stlb_entry); + address += MMU_STLB_4K_SIZE; + } + } + ReleaseMutex(g_mmu->page_table_mutex); + + /* Initial map info. */ + if (simple_map) + SMCreateNodes(); + else + CreateNode(); + + return MMU_STATUS_OK; +onerror: + /* Return status. */ + return status; +} + + +enum MMUStatus MMUInit(volatile unsigned char *hwregs) { + enum MMUStatus status; + unsigned i; + int result; + void *pointer; + + if (mmu_init == MMU_TRUE) { + /* All mmu use common table and dev, just initial once*/ + pr_notice(" *****MMU Already Initialed*****\n"); + return MMU_STATUS_OK; + } + + if(!hwregs || (ioread32((void*)(hwregs + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_NOT_FOUND; + + pr_notice(" *****MMU Init*****\n"); + + platformdev = platform_device_register_full(&hantro_platform_info); + if(platformdev == NULL) { + pr_err("hantrodec create platform device fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } else { + pr_info("Create platform device success\n"); + } + + result = platform_driver_register(&hantro_drm_platform_driver); + pr_notice("Platform driver status is %d\n", result); + + /* Allocate memory for the MMU object. */ + MMU_ON_ERROR(AllocateMemory(sizeof(struct MMU), &pointer)); + ZeroMemory(pointer, sizeof(struct MMU)); + + g_mmu = pointer; + + g_mmu->page_table_mutex = NULL; + + /* Create the page table mutex. */ + MMU_ON_ERROR(CreateMutex(&g_mmu->page_table_mutex)); + + for (i = 0; i < MMU_REGION_COUNT;i++) { + MMU_ON_ERROR(CreateMutex(&g_mmu->region[i].node_mutex)); + } + + mmu_init = MMU_TRUE; + return MMU_STATUS_OK; + +onerror: + pr_notice(" *****MMU Init Error*****\n"); + return status; +} + +enum MMUStatus MMURelease(void *filp, volatile unsigned char *hwregs) { + int i, j; + struct MMUNode *p, *tmp; + unsigned long long address; + unsigned int *page_table_entry; + + if(!hwregs || (ioread32((void*)(hwregs + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + + /* if mmu or TLB not enabled, return */ + if (simple_map) { + if(g_mmu == NULL || g_mmu->region[0].simple_map_head == NULL) + return MMU_STATUS_OK; + } else { + if(g_mmu == NULL || g_mmu->region[0].map_head == NULL) + return MMU_STATUS_OK; + } + + pr_notice(" *****MMU Release*****\n"); + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + + if (simple_map) { + for (i = 0; i < MMU_REGION_COUNT; i++) { + p = g_mmu->region[i].simple_map_head->next; + + while(p) { + tmp = p->next; + if(p->filp == (struct file *)filp) { + + for(j = 0;j < p->page_count; j++) { + GetPageEntry(p, &page_table_entry, j); + address = 0; + WritePageEntry(page_table_entry, address); + } + + SMRemoveNode(i, p->buf_virtual_address, p->process_id); + } + p = tmp; + } + } + } else { + for (i = 0; i < MMU_REGION_COUNT; i++) { + p = g_mmu->region[i].map_head->next; + + while(p) { + tmp = p->next; + if(p->filp == (struct file *)filp) { + + for(j = 0;j < p->page_count; j++) { + GetPageEntry(p, &page_table_entry, j); + address = 0; + WritePageEntry(page_table_entry, address); + } + + RemoveNode(i, p->buf_virtual_address, p->process_id); + } + p = tmp; + } + } + } + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; +} + +enum MMUStatus MMUCleanup(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + int i; + struct MMUNode *p, *tmp; + struct MMUNode *fp; + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL && + (ioread32((void*)(hwregs[i][0] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + if (hwregs[i][1] != NULL && + (ioread32((void*)(hwregs[i][1] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return MMU_STATUS_FALSE; + } + + pr_info(" *****MMU cleanup*****\n"); + if (pcie) { + if (g_mmu->stlb_virtual) + iounmap(g_mmu->stlb_virtual); + if (g_mmu->mtlb_virtual) + iounmap(g_mmu->mtlb_virtual); + if (g_mmu->page_table_array) + iounmap(g_mmu->page_table_array); + } else { + /* stlb_virtual is same alloc on alloc mtlb_virtual in func MMUEnable() + * so, should not free g_mmu->stlb_virtual.But free handle g_mmu->mtlb_physical + * size should be ( g_mmu->mtlb_size+g_mmu->stlb_size) + * */ + if (g_mmu->mtlb_virtual) + dma_free_coherent(&platformdev->dev, g_mmu->mtlb_size+g_mmu->stlb_size, + g_mmu->mtlb_virtual, (dma_addr_t)g_mmu->mtlb_physical); + if (g_mmu->page_table_array) + dma_free_coherent(&platformdev->dev, g_mmu->page_table_array_size, + g_mmu->page_table_array, (dma_addr_t)g_mmu->page_table_array_physical); + } + DeleteMutex(g_mmu->page_table_mutex); + + for (i = 0; i < MMU_REGION_COUNT; i++) { + DeleteMutex(g_mmu->region[i].node_mutex); + if (simple_map) { + p = g_mmu->region[i].simple_map_head; + while(p) { + tmp = p->next; + FreeMemory(p); + p = tmp; + MMUDEBUG(" *****clean node*****\n"); + } + } else { + fp = g_mmu->region[i].free_map_head; + p = g_mmu->region[i].map_head; + while(fp) { + tmp = fp->next; + FreeMemory(fp); + fp = tmp; + MMUDEBUG(" *****clean free node*****\n"); + } + + while(p) { + tmp = p->next; + FreeMemory(p); + p = tmp; + MMUDEBUG(" *****clean node*****\n"); + } + } + } + FreeMemory(g_mmu); + + platform_device_unregister(platformdev); + platform_driver_unregister(&hantro_drm_platform_driver); + pr_info("Unregister platform device.\n"); + + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) + iowrite32(0, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + if (hwregs[i][1] != NULL) + iowrite32(0, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + mmu_enable = 0; + mmu_init = 0; + + return MMU_STATUS_OK; +} + +/*------------------------------------------------------------------------------ + Function name: MMUEnable + Description: + Create TLB, set registers and enable MMU + + For pcie, TLB buffers come from FPGA memory and The distribution is as follows + MTLB: start from: 0x00100000, size: 4K bits + page table array: 0x00200000 64 bits + STLB: 0x00300000 4M bits + ------------------------------------------------------------------------------*/ +enum MMUStatus MMUEnable(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + enum MMUStatus status; + unsigned int address; + unsigned int mutex = MMU_FALSE; + dma_addr_t dma_handle; + u32 i = 0; + u32 address_ext; + u32 total_table_size; + + if(mmu_enable == MMU_TRUE) { + pr_info(" *****MMU Already Enabled*****\n"); + return MMU_STATUS_OK; + } + + pr_info(" *****MMU Enable...*****\n"); + + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + if(pcie) { + g_mmu->mtlb_size = MMU_MTLB_SIZE; + g_mmu->mtlb_virtual = ioremap(gBaseDDRHw+MTLB_PCIE_START_ADDRESS, g_mmu->mtlb_size); + MMUDEBUG("gBaseDDRHw=0x%llx, g_mmu->mtlb_virtual=0x%llx\n", gBaseDDRHw, g_mmu->mtlb_virtual); + g_mmu->mtlb_physical = MTLB_PCIE_START_ADDRESS; + + g_mmu->page_table_array = ioremap(gBaseDDRHw+PAGE_PCIE_START_ADDRESS, PAGE_TABLE_ENTRY_SIZE); + } else { + /* Allocate the 4K mode MTLB table. */ + total_table_size = MMU_MTLB_SIZE + MMU_MTLB_ENTRY_NUM*MMU_STLB_4K_SIZE; + g_mmu->mtlb_size = MMU_MTLB_SIZE; + g_mmu->mtlb_virtual = dma_alloc_coherent(&platformdev->dev, total_table_size, + &dma_handle, GFP_KERNEL | GFP_DMA); + MMUDEBUG(" *****g_mmu->mtlb_virtual = 0x%llx\n", g_mmu->mtlb_virtual); + g_mmu->mtlb_physical = (unsigned long long)dma_handle; + MMUDEBUG(" *****mtlb_physical = 0x%llx\n", (unsigned int)g_mmu->mtlb_physical); + if(g_mmu->mtlb_virtual == NULL) { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + + g_mmu->page_table_array_size = PAGE_TABLE_ENTRY_SIZE; + g_mmu->page_table_array = dma_alloc_coherent(&platformdev->dev, g_mmu->page_table_array_size, + &dma_handle, GFP_KERNEL | GFP_DMA); + MMUDEBUG(" *****g_mmu->page_table_array = 0x%llx\n", g_mmu->page_table_array); + g_mmu->page_table_array_physical = (unsigned long long)dma_handle; + MMUDEBUG(" *****page_table_array_physical = 0x%llx\n", (unsigned int)g_mmu->page_table_array_physical); + if(g_mmu->page_table_array == NULL) { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + } + + *((unsigned int*)g_mmu->page_table_array) = + (g_mmu->mtlb_physical & 0xFFFFFC00) | (0 << 0); + *((unsigned int *)g_mmu->page_table_array+1) = + (u32)(g_mmu->mtlb_physical >> 32)&0xff; + *((unsigned int *)g_mmu->page_table_array+2) = + (g_mmu->mtlb_physical & 0xFFFFFC00) | (0 << 0); + *((unsigned int *)g_mmu->page_table_array+3) = + (u32)(g_mmu->mtlb_physical >> 32)&0xff; + + MMUDEBUG(" Page table array[0]: lsb = 0x%08x\n", ((int *)g_mmu->page_table_array)[0]); + MMUDEBUG(" msb = 0x%08x\n", ((int *)g_mmu->page_table_array)[1]); + + ZeroMemory(g_mmu->mtlb_virtual, total_table_size); + + ReleaseMutex(g_mmu->page_table_mutex); + + MMU_ON_ERROR(SetupDynamicSpace()); + + if(pcie) { + address = PAGE_PCIE_START_ADDRESS; + } else { + address = g_mmu->page_table_array_physical; + address_ext = ((u32)(g_mmu->page_table_array_physical >> 32))&0xff; + } + +#ifndef HANTROVCMD_ENABLE_IP_SUPPORT + /* set regs of all MMUs */ + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) { + MMUDEBUG("hwregs[%d][0]=%p, id=0x%08x", i, hwregs[i][0], ioread32((void*)hwregs[i][0] + MMU_REG_HW_ID)); + iowrite32(address, (void*)(hwregs[i][0] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][0] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + } + if (hwregs[i][1] != NULL) { + MMUDEBUG("hwregs[%d][1]=%p, id=0x%08x", i, hwregs[i][1], ioread32((void*)hwregs[i][1] + MMU_REG_HW_ID)); + iowrite32(address, (void*)(hwregs[i][1] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][1] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + } +#endif + mmu_enable = MMU_TRUE; + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Enable Error*****\n"); + return status; +} + +/*------------------------------------------------------------------------------ + Function name: MMUFlush + Description: + Flush MMU reg to update cache in MMU. + ------------------------------------------------------------------------------*/ +static enum MMUStatus MMUFlush(u32 core_id, volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) { + enum MMUStatus status; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Flush*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + if (hwregs[core_id][0] != NULL) { + iowrite32(0x10, (void*)(hwregs[core_id][0] + MMU_REG_FLUSH)); + iowrite32(0x00, (void*)(hwregs[core_id][0] + MMU_REG_FLUSH)); + } else { + pr_err("hantrodec alloc buffer fail\n"); + status = MMU_STATUS_FALSE; + goto onerror; + } + if (hwregs[core_id][1] != NULL) { + iowrite32(0x10, (void*)(hwregs[core_id][1] + MMU_REG_FLUSH)); + iowrite32(0x00, (void*)(hwregs[core_id][1] + MMU_REG_FLUSH)); + } + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Flush Error*****\n"); + return status; +} + +static enum MMUStatus MMUMemNodeMap(struct addr_desc *addr, struct file *filp) { + enum MMUStatus status; + unsigned int page_count = 0; + unsigned int i = 0; + struct MMUNode *p; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + enum MMURegion e; + unsigned int mutex = MMU_FALSE; + u32 ext_addr; + + MMUDEBUG(" *****MMU Map*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + page_count = (addr->size - 1)/PAGE_SIZE + 1; + + GetPhysicalAddress(addr->virtual_address, &address); + MMUDEBUG(" *****MMU map address*****%x\n", address); + if(address >= REGION_IN_START && + address + addr->size < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address + addr->size < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address + addr->size < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) { + MMU_ON_ERROR(SMCheckAddress(e, addr->virtual_address)); + + SMCreateNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", page_count); + + p->buf_virtual_address = addr->virtual_address; + p->process_id = GetProcessID(); + p->filp = filp; + + p->mtlb_start = ((address + map_shift) >> MMU_MTLB_SHIFT); + p->stlb_start = ((address + map_shift) >> MMU_STLB_4K_SHIFT ) & 0x3FF; + p->mtlb_end = (page_count + p->stlb_start) / MMU_STLB_4K_ENTRY_NUM + + p->mtlb_start; + p->stlb_end = (page_count + p->stlb_start) % MMU_STLB_4K_ENTRY_NUM; + p->page_count = page_count; + + for(i = 0;i < page_count; i++) { + GetPhysicalAddress(addr->virtual_address + i * PAGE_SIZE, &address); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + address = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, address); + } + + /* Purpose of Bare_metal mode: input bus address==mmu address*/ + addr->bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } else { + MMU_ON_ERROR(FindFreeNode(e, &p, page_count)); + + SplitFreeNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", p->page_count); + + p->buf_virtual_address = addr->virtual_address; + p->process_id = GetProcessID(); + p->filp = filp; + + for(i = 0;i < page_count; i++) { + GetPhysicalAddress(addr->virtual_address + i * PAGE_SIZE, &address); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + address = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, address); + } + addr->bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } + MMUDEBUG(" MMU_MTLB_SHIFT %d MMU_STLB_4K_SHIFT %d\n", MMU_MTLB_SHIFT, MMU_STLB_4K_SHIFT); + MMUDEBUG(" MMUMemNodeMap map total %d pages in region %d\nMTLB/STLB starts %d/%d, MTLB/STLB ends %d/%d\n", + page_count, (u32)e, p->mtlb_start, p->stlb_start, p->mtlb_end, p->stlb_end); + MMUDEBUG(" MMUMemNodeMap map %p -> 0x%08x\n", addr->virtual_address, addr->bus_address); + + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Map Error*****\n"); + return status; +} + +static enum MMUStatus MMUMemNodeUnmap(struct addr_desc *addr) { + unsigned int i; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + int process_id = GetProcessID(); + enum MMURegion e = MMU_REGION_COUNT; + enum MMUStatus status = MMU_STATUS_OUT_OF_MEMORY; + struct MMUNode *p; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Unmap*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + GetPhysicalAddress(addr->virtual_address, &address); + if(address >= REGION_IN_START && + address < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) + p = g_mmu->region[e].simple_map_head->next; + else + p = g_mmu->region[e].map_head->next; + /* Reset STLB of the node */ + while(p) { + if(p->buf_virtual_address == addr->virtual_address && + p->process_id == process_id) { + for(i = 0;i < p->page_count; i++) { + GetPageEntry(p, &page_table_entry, i); + address = 0; + WritePageEntry(page_table_entry, address); + } + break; + } + p = p->next; + } + if(!p) + goto onerror; + + if (simple_map) + SMRemoveNode(e, addr->virtual_address, process_id); + else + RemoveNode(e, addr->virtual_address, process_id); + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Unmap Error*****\n"); + return status; +} + +enum MMUStatus MMUKernelMemNodeMap(struct kernel_addr_desc *addr) { + enum MMUStatus status; + unsigned int page_count = 0; + unsigned int i = 0; + struct MMUNode *p; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + enum MMURegion e; + unsigned int mutex = MMU_FALSE; + u32 ext_addr; + u32 page_entry_value = 0; + + MMUDEBUG(" *****MMU Map*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + page_count = (addr->size - 1)/PAGE_SIZE + 1; + + address = addr->bus_address; + MMUDEBUG(" *****MMU map address*****%x\n", address); + if(address >= REGION_IN_START && + address + addr->size < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address + addr->size < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address + addr->size < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) { + //TODO: should check bus addr + //MMU_ON_ERROR(SMCheckAddress(e, addr->virtual_address)); + + SMCreateNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", page_count); + + p->buf_bus_address = addr->bus_address; + p->process_id = GetProcessID(); + p->filp = NULL; + + p->mtlb_start = ((address + map_shift) >> MMU_MTLB_SHIFT); + p->stlb_start = ((address + map_shift) >> MMU_STLB_4K_SHIFT ) & 0x3FF; + p->mtlb_end = (page_count + p->stlb_start) / MMU_STLB_4K_ENTRY_NUM + + p->mtlb_start; + p->stlb_end = (page_count + p->stlb_start) % MMU_STLB_4K_ENTRY_NUM; + p->page_count = page_count; + + for(i = 0;i < page_count; i++) { + /* this function used in kernel only, so we think it's a contunuous buffer*/ + address += (i ? PAGE_SIZE : 0); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + + /* Purpose of Bare_metal mode: input bus address==mmu address*/ + addr->mmu_bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } else { + MMU_ON_ERROR(FindFreeNode(e, &p, page_count)); + + SplitFreeNode(e, &p, page_count); + MMUDEBUG(" *****Node map size*****%d\n", p->page_count); + + p->buf_bus_address = addr->bus_address; + p->process_id = GetProcessID(); + p->filp = NULL; + + for(i = 0;i < page_count; i++) { + /* this function used in kernel only, so we think it's a contunuous buffer*/ + address += (i ? PAGE_SIZE : 0); + GetPageEntry(p, &page_table_entry, i); + ext_addr = ((u32)(address>>32))&0xff; + page_entry_value = (address & 0xFFFFF000) + /* ext address , physical address bits [39,32]*/ + | (ext_addr << 4) + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + WritePageEntry(page_table_entry, page_entry_value); + } + addr->mmu_bus_address = p->mtlb_start << MMU_MTLB_SHIFT + | p->stlb_start << MMU_STLB_4K_SHIFT; + } + MMUDEBUG(" KERNEL MMU_MTLB_SHIFT %d MMU_STLB_4K_SHIFT %d\n", MMU_MTLB_SHIFT, MMU_STLB_4K_SHIFT); + MMUDEBUG(" MMUKernelMemNodeMap map total %d pages in region %d\nMTLB/STLB starts %d/%d, MTLB/STLB ends %d/%d\n", + page_count, (u32)e, p->mtlb_start, p->stlb_start, p->mtlb_end, p->stlb_end); + MMUDEBUG(" MMUKernelMemNodeMap map 0x%llx -> 0x%08x\n", addr->bus_address, addr->mmu_bus_address); + + ReleaseMutex(g_mmu->page_table_mutex); + + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Map Error*****\n"); + return status; +} + +enum MMUStatus MMUKernelMemNodeUnmap(struct kernel_addr_desc *addr) { + unsigned int i; + unsigned long long address = 0x0; + unsigned int *page_table_entry; + int process_id = GetProcessID(); + enum MMURegion e = MMU_REGION_COUNT; + enum MMUStatus status = MMU_STATUS_OUT_OF_MEMORY; + struct MMUNode *p; + unsigned int mutex = MMU_FALSE; + + MMUDEBUG(" *****MMU Unmap*****\n"); + AcquireMutex(g_mmu->page_table_mutex, MMU_INFINITE); + mutex = MMU_TRUE; + + address = addr->bus_address; + if(address >= REGION_IN_START && + address < REGION_IN_END) + e = MMU_REGION_IN; + else if(address >= REGION_OUT_START && + address < REGION_OUT_END) + e = MMU_REGION_OUT; + else if(address >= REGION_PRIVATE_START && + address < REGION_PRIVATE_END) + e = MMU_REGION_PRIVATE; + else + e = MMU_REGION_PUB; + + if (simple_map) + p = g_mmu->region[e].simple_map_head->next; + else + p = g_mmu->region[e].map_head->next; + /* Reset STLB of the node */ + while(p) { + if(p->buf_bus_address == addr->bus_address && + p->process_id == process_id) { + for(i = 0;i < p->page_count; i++) { + GetPageEntry(p, &page_table_entry, i); + address = 0; + WritePageEntry(page_table_entry, address); + } + break; + } + p = p->next; + } + if(!p) + goto onerror; + + if (simple_map) + SMRemoveKernelNode(e, addr->bus_address, process_id); + else + RemoveKernelNode(e, addr->bus_address, process_id); + + ReleaseMutex(g_mmu->page_table_mutex); + return MMU_STATUS_OK; + +onerror: + if (mutex) { + ReleaseMutex(g_mmu->page_table_mutex); + } + MMUDEBUG(" *****MMU Unmap Error*****\n"); + return status; +} + + +static long MMUCtlBufferMap(struct file *filp, unsigned long arg) { + struct addr_desc addr; + long tmp; + + tmp = copy_from_user(&addr, (void*)arg, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUMemNodeMap(&addr, filp); + + tmp = copy_to_user((void*) arg, &addr, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_to_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + return 0; +} + +static long MMUCtlBufferUnmap(unsigned long arg) { + struct addr_desc addr; + long tmp; + + tmp = copy_from_user(&addr, (void*)arg, sizeof(struct addr_desc)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUMemNodeUnmap(&addr); + return 0; +} + +static long MMUCtlEnable(unsigned long arg, volatile unsigned char *hwregs[HXDEC_MAX_CORES][2]) { + unsigned int enable; + long tmp; + + tmp = copy_from_user(&enable, (void*)arg, sizeof(unsigned int)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUEnable(hwregs); + + return 0; +} + +static long MMUCtlFlush(unsigned long arg, volatile unsigned char *hwregs[HXDEC_MAX_CORES][2]) { + unsigned int core_id; + long tmp; + + tmp = copy_from_user(&core_id, (void*)arg, sizeof(unsigned int)); + if (tmp) { + MMUDEBUG("copy_from_user failed, returned %li\n", tmp); + return -MMU_EFAULT; + } + + MMUFlush(core_id, hwregs); + + return 0; +} + +long MMUIoctl(unsigned int cmd, void *filp, unsigned long arg, + volatile unsigned char *hwregs[HXDEC_MAX_CORES][2]) { + + u32 i = 0; + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL && + (ioread32((void*)(hwregs[i][0] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return -MMU_ENOTTY; + + if (hwregs[i][1] != NULL && + (ioread32((void*)(hwregs[i][1] + MMU_REG_HW_ID))>>16) != 0x4D4D) + return -MMU_ENOTTY; + MMUDEBUG("mmu_hwregs[%d][0].mmu_hwregs[0]=%p", i, hwregs[i][0]); + MMUDEBUG("mmu_hwregs[%d][1].mmu_hwregs[0]=%p", i, hwregs[i][1]); + } + + switch (cmd) { + case HANTRO_IOCS_MMU_MEM_MAP: { + return (MMUCtlBufferMap((struct file *)filp, arg)); + } + case HANTRO_IOCS_MMU_MEM_UNMAP: { + return (MMUCtlBufferUnmap(arg)); + } + case HANTRO_IOCS_MMU_ENABLE: { + return (MMUCtlEnable(arg, hwregs)); + } + case HANTRO_IOCS_MMU_FLUSH: { + return (MMUCtlFlush(arg, hwregs)); + } + default: + return -MMU_ENOTTY; + } +} + +unsigned long long GetMMUAddress(void) +{ + unsigned long long address = 0; + if(pcie) + address = PAGE_PCIE_START_ADDRESS; + else + address = g_mmu->page_table_array_physical; + + return address; +} + +void MMURestore(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]) +{ + if (g_mmu == NULL) + return; + + int i; + unsigned int address; + u32 address_ext; + address = g_mmu->page_table_array_physical; + address_ext = ((u32)(g_mmu->page_table_array_physical >> 32))&0xff; + for (i = 0; i < MAX_SUBSYS_NUM; i++) { + if (hwregs[i][0] != NULL) { + iowrite32(address, (void*)(hwregs[i][0] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][0] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][0] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][0] + MMU_REG_CONTROL)); + } + if (hwregs[i][1] != NULL) { + iowrite32(address, (void*)(hwregs[i][1] + MMU_REG_ADDRESS)); + iowrite32(address_ext, (void *)(hwregs[i][1] + MMU_REG_ADDRESS_MSB)); + + iowrite32(0x10000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + iowrite32(0x00000, (void*)(hwregs[i][1] + MMU_REG_PAGE_TABLE_ID)); + + iowrite32(1, (void*)(hwregs[i][1] + MMU_REG_CONTROL)); + } + } +} diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantrommu.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantrommu.h new file mode 100755 index 000000000..6c2184f80 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/hantrommu.h @@ -0,0 +1,155 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _HANTROMMU_H_ +#define _HANTROMMU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __FREERTOS__ +#elif defined(__linux__) +#include +#endif + +#define REGION_IN_START 0x0 +#define REGION_IN_END 0x0 +#define REGION_OUT_START 0x0 +#define REGION_OUT_END 0x0 +#define REGION_PRIVATE_START 0x0 +#define REGION_PRIVATE_END 0x0 + +#define REGION_IN_MMU_START 0x1000 +#define REGION_IN_MMU_END 0x40002000 +#define REGION_OUT_MMU_START 0x40002000 +#define REGION_OUT_MMU_END 0x40002000 +#define REGION_PRIVATE_MMU_START 0x40002000 +#define REGION_PRIVATE_MMU_END 0x40002000 + +#define MMU_REG_OFFSET 0 +#define MMU_REG_HW_ID (MMU_REG_OFFSET + 6*4) +#define MMU_REG_FLUSH (MMU_REG_OFFSET + 97*4) +#define MMU_REG_PAGE_TABLE_ID (MMU_REG_OFFSET + 107*4) +#define MMU_REG_CONTROL (MMU_REG_OFFSET + 226*4) +#define MMU_REG_ADDRESS (MMU_REG_OFFSET + 227*4) +#define MMU_REG_ADDRESS_MSB (MMU_REG_OFFSET + 228*4) + +#define MTLB_PCIE_START_ADDRESS 0x00100000 +#define PAGE_PCIE_START_ADDRESS 0x00200000 /* page_table_entry start address */ +#define STLB_PCIE_START_ADDRESS 0x00300000 +#define PAGE_TABLE_ENTRY_SIZE 64 + +enum MMUStatus { + MMU_STATUS_OK = 0, + + MMU_STATUS_FALSE = -1, + MMU_STATUS_INVALID_ARGUMENT = -2, + MMU_STATUS_INVALID_OBJECT = -3, + MMU_STATUS_OUT_OF_MEMORY = -4, + MMU_STATUS_NOT_FOUND = -19, +}; + +struct addr_desc { + void *virtual_address; /* buffer virtual address */ + unsigned int bus_address; /* buffer physical address */ + unsigned int size; /* physical size */ +}; + +struct kernel_addr_desc { + unsigned long long bus_address; /* buffer virtual address */ + unsigned int mmu_bus_address; /* buffer physical address in MMU*/ + unsigned int size; /* physical size */ +}; + + +#define HANTRO_IOC_MMU 'm' + +#define HANTRO_IOCS_MMU_MEM_MAP _IOWR(HANTRO_IOC_MMU, 1, struct addr_desc *) +#define HANTRO_IOCS_MMU_MEM_UNMAP _IOWR(HANTRO_IOC_MMU, 2, struct addr_desc *) +#define HANTRO_IOCS_MMU_ENABLE _IOWR(HANTRO_IOC_MMU, 3, unsigned int *) +#define HANTRO_IOCS_MMU_FLUSH _IOWR(HANTRO_IOC_MMU, 4, unsigned int *) +#define HANTRO_IOC_MMU_MAXNR 4 + +#define MAX_SUBSYS_NUM 4 /* up to 4 subsystem (temporary) */ +#define HXDEC_MAX_CORES MAX_SUBSYS_NUM /* used in hantro_dec.c */ + +/* Init MMU, should be called in driver init function. */ +enum MMUStatus MMUInit(volatile unsigned char *hwregs); +/* Clean up all data in MMU, should be called in driver cleanup function + when rmmod driver*/ +enum MMUStatus MMUCleanup(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); +/* The function should be called in driver realease function + when driver exit unnormally */ +enum MMUStatus MMURelease(void *filp, volatile unsigned char *hwregs); + +enum MMUStatus MMUEnable(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +/* Used in kernel to map buffer */ +enum MMUStatus MMUKernelMemNodeMap(struct kernel_addr_desc *addr); + +/* Used in kernel to unmap buffer */ +enum MMUStatus MMUKernelMemNodeUnmap(struct kernel_addr_desc *addr); + +unsigned long long GetMMUAddress(void); +long MMUIoctl(unsigned int cmd, void *filp, unsigned long arg, + volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +void MMURestore(volatile unsigned char *hwregs[MAX_SUBSYS_NUM][2]); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.c new file mode 100644 index 000000000..73a694036 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.c @@ -0,0 +1,99 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)) +#include +#else +#include +#endif +#include +#include + +#include +#include "vc8000_axife.h" +/* mode description + * 1: OYB normal(enable) + * 2: bypass +*/ +u32 AXIFEEnable(volatile u8 *hwregs, u32 mode) { +#ifndef HANTROVCMD_ENABLE_IP_SUPPORT + if (!hwregs) return -1; + + //AXI FE pass through + if(mode == 1) + { + iowrite32(0x02, (void *)(hwregs+AXI_REG10_SW_FRONTEND_EN)); + iowrite32(0x00, (void *)(hwregs+AXI_REG11_SW_WORK_MODE)); + } + else if(mode == 2) + { + iowrite32(0x02, (void *)(hwregs+AXI_REG10_SW_FRONTEND_EN)); + iowrite32(0x40, (void *)(hwregs+AXI_REG11_SW_WORK_MODE)); + } + printk(KERN_INFO "AXIFEEnable: axife_reg10_addr=0x%p, *axife_reg10_addr=0x%08x\n", hwregs + 10*4, ioread32((void *)(hwregs + 10*4))); + printk(KERN_INFO "AXIFEEnable: axife_reg11_addr=0x%p, *axife_reg11_addr=0x%08x\n", hwregs + 11*4, ioread32((void *)(hwregs + 11*4))); +#endif + return 0; +} diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.h new file mode 100644 index 000000000..a43081419 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_axife.h @@ -0,0 +1,77 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef __VC8000_AXIFE_H__ +#define __VC8000_AXIFE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __FREERTOS__ +#elif defined(__linux__) +#include +#include +#endif + +#define AXI_REG10_SW_FRONTEND_EN 10*4 //0x28 +#define AXI_REG11_SW_WORK_MODE 11*4 //0x2c + +u32 AXIFEEnable(volatile u8 *hwregs, u32 mode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.c new file mode 100644 index 000000000..a3a14a6bf --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.c @@ -0,0 +1,102 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +#include +#include +#include +#include +#include "vc8000_driver.h" + +static u32 vcmd_supported = 1; +int __init hantroenc_normal_init(void); +int __init hantroenc_vcmd_init(void); +void __exit hantroenc_normal_cleanup(void); +void __exit hantroenc_vcmd_cleanup(void); + +int __init hantroenc_init(void) +{ + if(vcmd_supported==0) + { + return hantroenc_normal_init(); + } + else + { + return hantroenc_vcmd_init(); + } +} + +void __exit hantroenc_cleanup(void) +{ + if(vcmd_supported==0) + { + hantroenc_normal_cleanup(); + } + else + { + hantroenc_vcmd_cleanup(); + } + return; +} + + +module_init(hantroenc_init); +module_exit(hantroenc_cleanup); +module_param(vcmd_supported,uint,0); + +/* module description */ +/*MODULE_LICENSE("Proprietary");*/ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Verisilicon"); +MODULE_DESCRIPTION("VC8000 Vcmd driver"); + + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.h new file mode 100644 index 000000000..ff818889a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_driver.h @@ -0,0 +1,331 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#ifndef _VC8000_VCMD_DRIVER_H_ +#define _VC8000_VCMD_DRIVER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __FREERTOS__ +/* needed for the _IOW etc stuff used later */ +#include "base_type.h" +#include "osal.h" +#include "dev_common_freertos.h" +#elif defined(__linux__) +#include /* needed for the _IOW etc stuff used later */ +#else //For other os +//TODO... +#endif +#ifdef HANTROMMU_SUPPORT +#include "hantrommu.h" +#endif + +#ifdef HANTROAXIFE_SUPPORT +#include "vc8000_axife.h" +#endif + +#ifdef __FREERTOS__ +//ptr_t has been defined in base_type.h //Now the FreeRTOS mem need to support 64bit env +#elif defined(__linux__) +#undef ptr_t +#define ptr_t PTR_T_KERNEL +typedef size_t ptr_t; +#endif +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef HANTRO_DRIVER_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "vc8000: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) printf(__FILE__ ":%d: " fmt, __LINE__ , ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#define ENC_HW_ID1 0x48320100 +#define ENC_HW_ID2 0x80006000 +#define CORE_INFO_MODE_OFFSET 31 +#define CORE_INFO_AMOUNT_OFFSET 28 + +/* Use 'k' as magic number */ +#define HANTRO_IOC_MAGIC 'k' + +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ + +#define HANTRO_IOCG_HWOFFSET _IOR(HANTRO_IOC_MAGIC, 3, unsigned long *) +#define HANTRO_IOCG_HWIOSIZE _IOR(HANTRO_IOC_MAGIC, 4, unsigned int *) +#define HANTRO_IOC_CLI _IO(HANTRO_IOC_MAGIC, 5) +#define HANTRO_IOC_STI _IO(HANTRO_IOC_MAGIC, 6) +#define HANTRO_IOCX_VIRT2BUS _IOWR(HANTRO_IOC_MAGIC, 7, unsigned long *) +#define HANTRO_IOCH_ARDRESET _IO(HANTRO_IOC_MAGIC, 8) /* debugging tool */ +#define HANTRO_IOCG_SRAMOFFSET _IOR(HANTRO_IOC_MAGIC, 9, unsigned long *) +#define HANTRO_IOCG_SRAMEIOSIZE _IOR(HANTRO_IOC_MAGIC, 10, unsigned int *) +#define HANTRO_IOCH_ENC_RESERVE _IOR(HANTRO_IOC_MAGIC, 11,unsigned int *) +#define HANTRO_IOCH_ENC_RELEASE _IOR(HANTRO_IOC_MAGIC, 12,unsigned int *) +#define HANTRO_IOCG_CORE_NUM _IOR(HANTRO_IOC_MAGIC, 13,unsigned int *) +#define HANTRO_IOCG_CORE_INFO _IOR(HANTRO_IOC_MAGIC, 14,SUBSYS_CORE_INFO *) +#define HANTRO_IOCG_CORE_WAIT _IOR(HANTRO_IOC_MAGIC, 15, unsigned int *) +#define HANTRO_IOCG_ANYCORE_WAIT _IOR(HANTRO_IOC_MAGIC, 16, CORE_WAIT_OUT *) + +#define HANTRO_IOCH_GET_CMDBUF_PARAMETER _IOWR(HANTRO_IOC_MAGIC, 25,struct cmdbuf_mem_parameter *) +#define HANTRO_IOCH_GET_CMDBUF_POOL_SIZE _IOWR(HANTRO_IOC_MAGIC, 26,unsigned long) +#define HANTRO_IOCH_SET_CMDBUF_POOL_BASE _IOWR(HANTRO_IOC_MAGIC, 27,unsigned long) +#define HANTRO_IOCH_GET_VCMD_PARAMETER _IOWR(HANTRO_IOC_MAGIC, 28, struct config_parameter *) +#define HANTRO_IOCH_RESERVE_CMDBUF _IOWR(HANTRO_IOC_MAGIC, 29,struct exchange_parameter *) +#define HANTRO_IOCH_LINK_RUN_CMDBUF _IOR(HANTRO_IOC_MAGIC, 30,u16 *) +#define HANTRO_IOCH_WAIT_CMDBUF _IOR(HANTRO_IOC_MAGIC, 31,u16 *) +#define HANTRO_IOCH_RELEASE_CMDBUF _IOR(HANTRO_IOC_MAGIC, 32,u16 *) +#define HANTRO_IOCH_POLLING_CMDBUF _IOR(HANTRO_IOC_MAGIC, 33,u16 *) + +#define HANTRO_IOCH_GET_VCMD_ENABLE _IOWR(HANTRO_IOC_MAGIC, 50,unsigned long) + +#define GET_ENCODER_IDX(type_info) (CORE_VC8000E); +#define CORETYPE(core) (1 << core) +#define HANTRO_IOC_MAXNR 60 + +/*priority support*/ + +#define MAX_CMDBUF_PRIORITY_TYPE 2 //0:normal priority,1:high priority + +#define CMDBUF_PRIORITY_NORMAL 0 +#define CMDBUF_PRIORITY_HIGH 1 + +#define OPCODE_WREG (0x01<<27) +#define OPCODE_END (0x02<<27) +#define OPCODE_NOP (0x03<<27) +#define OPCODE_RREG (0x16<<27) +#define OPCODE_INT (0x18<<27) +#define OPCODE_JMP (0x19<<27) +#define OPCODE_STALL (0x09<<27) +#define OPCODE_CLRINT (0x1a<<27) +#define OPCODE_JMP_RDY0 (0x19<<27) +#define OPCODE_JMP_RDY1 ((0x19<<27)|(1<<26)) +#define JMP_IE_1 (1<<25) +#define JMP_RDY_1 (1<<26) + + +#define CLRINT_OPTYPE_READ_WRITE_1_CLEAR 0 +#define CLRINT_OPTYPE_READ_WRITE_0_CLEAR 1 +#define CLRINT_OPTYPE_READ_CLEAR 2 + +#define VC8000E_FRAME_RDY_INT_MASK 0x0001 +#define VC8000E_CUTREE_RDY_INT_MASK 0x0002 +#define VC8000E_DEC400_INT_MASK 0x0004 +#define VC8000E_L2CACHE_INT_MASK 0x0008 +#define VC8000E_MMU_INT_MASK 0x0010 +#define CUTREE_MMU_INT_MASK 0x0020 + + +#define VC8000D_FRAME_RDY_INT_MASK 0x0100 +#define VC8000D_DEC400_INT_MASK 0x0400 +#define VC8000D_L2CACHE_INT_MASK 0x0800 +#define VC8000D_MMU_INT_MASK 0x1000 + + +#define VC8000D_DEC400_INT_MASK_1_1_1 0x0200 +#define VC8000D_L2CACHE_INT_MASK_1_1_1 0x0400 +#define VC8000D_MMU_INT_MASK_1_1_1 0x0800 + + + + +#define HW_ID_1_0_C 0x43421001 +#define HW_ID_1_1_2 0x43421102 + + +#define ASIC_STATUS_SEGMENT_READY 0x1000 +#define ASIC_STATUS_FUSE_ERROR 0x200 +#define ASIC_STATUS_SLICE_READY 0x100 +#define ASIC_STATUS_LINE_BUFFER_DONE 0x080 /* low latency */ +#define ASIC_STATUS_HW_TIMEOUT 0x040 +#define ASIC_STATUS_BUFF_FULL 0x020 +#define ASIC_STATUS_HW_RESET 0x010 +#define ASIC_STATUS_ERROR 0x008 +#define ASIC_STATUS_FRAME_READY 0x004 +#define ASIC_IRQ_LINE 0x001 +#define ASIC_STATUS_ALL (ASIC_STATUS_SEGMENT_READY |\ + ASIC_STATUS_FUSE_ERROR |\ + ASIC_STATUS_SLICE_READY |\ + ASIC_STATUS_LINE_BUFFER_DONE |\ + ASIC_STATUS_HW_TIMEOUT |\ + ASIC_STATUS_BUFF_FULL |\ + ASIC_STATUS_HW_RESET |\ + ASIC_STATUS_ERROR |\ + ASIC_STATUS_FRAME_READY) + +enum +{ + CORE_VC8000E = 0, + CORE_VC8000EJ = 1, + CORE_CUTREE = 2, + CORE_DEC400 = 3, + CORE_MMU = 4, + CORE_L2CACHE = 5, + CORE_AXIFE = 6, + CORE_APBFT = 7, + CORE_MMU_1 = 8, + CORE_AXIFE_1 = 9, + CORE_MAX +}; + +//#define CORE_MAX (CORE_MMU) + +/*module_type support*/ + +enum vcmd_module_type{ + VCMD_TYPE_ENCODER = 0, + VCMD_TYPE_CUTREE, + VCMD_TYPE_DECODER, + VCMD_TYPE_JPEG_ENCODER, + VCMD_TYPE_JPEG_DECODER, + MAX_VCMD_TYPE +}; + +struct cmdbuf_mem_parameter +{ + u32 *virt_cmdbuf_addr; + ptr_t phy_cmdbuf_addr; //cmdbuf pool base physical address + u32 mmu_phy_cmdbuf_addr; //cmdbuf pool base mmu mapping address + u32 cmdbuf_total_size; //cmdbuf pool total size in bytes. + u16 cmdbuf_unit_size; //one cmdbuf size in bytes. all cmdbuf have same size. + u32 *virt_status_cmdbuf_addr; + ptr_t phy_status_cmdbuf_addr; //status cmdbuf pool base physical address + u32 mmu_phy_status_cmdbuf_addr; //status cmdbuf pool base mmu mapping address + u32 status_cmdbuf_total_size; //status cmdbuf pool total size in bytes. + u16 status_cmdbuf_unit_size; //one status cmdbuf size in bytes. all status cmdbuf have same size. + ptr_t base_ddr_addr; //for pcie interface, hw can only access phy_cmdbuf_addr-pcie_base_ddr_addr. + //for other interface, this value should be 0? +}; + +struct config_parameter +{ + u16 module_type; //input vc8000e=0,cutree=1,vc8000d=2,jpege=3, jpegd=4 + u16 vcmd_core_num; //output, how many vcmd cores are there with corresponding module_type. + u16 submodule_main_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_dec400_addr; //output ,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_L2Cache_addr; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_MMU_addr[2]; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 submodule_axife_addr[2]; //output,if submodule addr == 0xffff, this submodule does not exist. + u16 config_status_cmdbuf_id; // output , this status comdbuf save the all register values read in driver init.//used for analyse configuration in cwl. + u32 vcmd_hw_version_id; +}; + +/*need to consider how many memory should be allocated for status.*/ +struct exchange_parameter +{ + u32 executing_time; //input ;executing_time=encoded_image_size*(rdoLevel+1)*(rdoq+1); + u16 module_type; //input input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4 + u16 cmdbuf_size; //input, reserve is not used; link and run is input. + u16 priority; //input,normal=0, high/live=1 + u16 cmdbuf_id; //output ,it is unique in driver. + u16 core_id; //just used for polling. +}; + +typedef struct CoreWaitOut +{ + u32 job_id[4]; + u32 irq_status[4]; + u32 irq_num; +} CORE_WAIT_OUT; + +typedef struct +{ + u32 subsys_idx; + u32 core_type; + unsigned long offset; + u32 reg_size; + int irq; +}CORE_CONFIG; + +typedef struct +{ + unsigned long base_addr; + u32 iosize; + u32 resouce_shared; //indicate the core share resources with other cores or not.If 1, means cores can not work at the same time. +}SUBSYS_CONFIG; + +typedef struct +{ + u32 type_info; //indicate which IP is contained in this subsystem and each uses one bit of this variable + unsigned long offset[CORE_MAX]; + unsigned long regSize[CORE_MAX]; + int irq[CORE_MAX]; +}SUBSYS_CORE_INFO; + +typedef struct +{ + SUBSYS_CONFIG cfg; + SUBSYS_CORE_INFO core_info; +}SUBSYS_DATA; + +#ifdef __cplusplus +} +#endif + +#endif /* !_VC8000_VCMD_DRIVER_H_ */ diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_normal_driver.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_normal_driver.c new file mode 100644 index 000000000..bc6a45c4a --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_normal_driver.c @@ -0,0 +1,1458 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +/* request_irq(), free_irq() */ +#include +#include + +#include +#include +/* needed for virt_to_phys() */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* our own stuff */ +#include "vc8000_driver.h" + +unsigned long gBaseDDRHw = 0; +unsigned int pcie = 0; /* used in hantro_mmu.c*/ + +//#define MULTI_THR_TEST +#ifdef MULTI_THR_TEST + +#define WAIT_NODE_NUM 32 +struct wait_list_node +{ + u32 node_id; //index of the node + u32 used_flag; //1:the node is insert to the wait queue list. + u32 sem_used; //1:the source is released and the semphone is uped. + struct semaphore wait_sem; //the unique semphone for per reserve_encoder thread. + u32 wait_cond; //the condition for wait. Equal to the "core_info". + struct list_head wait_list; //list node. +}; +static struct list_head reserve_header; +static struct wait_list_node res_wait_node[WAIT_NODE_NUM]; + +static void wait_delay(unsigned int delay) { + if(delay > 0) { + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) + ktime_t dl = ktime_set((delay / MSEC_PER_SEC), + (delay % MSEC_PER_SEC) * NSEC_PER_MSEC); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&dl, HRTIMER_MODE_REL); + #else + msleep(delay); + #endif + } +} + +static u32 request_wait_node(struct wait_list_node **node,u32 start_id) +{ + u32 i; + struct wait_list_node *temp_node; + while(1) + { + for(i=start_id;iused_flag==0) + { + temp_node->used_flag=1; + *node = temp_node; + return i; + } + } + wait_delay(10); + } +} + +static void request_wait_sema(struct wait_list_node **node) +{ + u32 i; + struct wait_list_node *temp_node; + while(1) + { + for(i=0;iused_flag==0)&&(temp_node->sem_used==0)) + { + temp_node->sem_used =1; + *node = temp_node; + return ; + } + } + wait_delay(10); + } +} + +static void init_wait_node(struct wait_list_node *node,u32 cond, u32 sem_flag) +{ + node->used_flag = 0; + node->wait_cond = cond; + sema_init(&node->wait_sem, sem_flag); + INIT_LIST_HEAD(&node->wait_list); + if(sem_flag>0) + { + node->sem_used =1; + } +} + +static void init_reserve_wait(u32 dev_num) +{ + u32 i; + u32 cond = 0x80000001; + u32 sem_flag =0; + struct wait_list_node *node; + +// printk("%s,%d, dev_num %d\n",__FUNCTION__,__LINE__,dev_num); + + INIT_LIST_HEAD(&reserve_header); + + for(i=0;inode_id = i; + init_wait_node(node,cond,sem_flag); + } +} + +void release_reserve_wait(u32 dev_num) +{ + +} + +#endif +/********variables declaration related with race condition**********/ + +struct semaphore enc_core_sem; +DECLARE_WAIT_QUEUE_HEAD(hw_queue); +DEFINE_SPINLOCK(owner_lock); +DECLARE_WAIT_QUEUE_HEAD(enc_wait_queue); + +/*------------------------------------------------------------------------ +*****************************PORTING LAYER******************************** +-------------------------------------------------------------------------*/ +#define RESOURCE_SHARED_INTER_SUBSYS 0 /*0:no resource sharing inter subsystems 1: existing resource sharing*/ +#define SUBSYS_0_IO_ADDR 0x90000 /*customer specify according to own platform*/ +#define SUBSYS_0_IO_SIZE (1024 * 4) /* bytes */ + +#define SUBSYS_1_IO_ADDR 0xA0000 /*customer specify according to own platform*/ +#define SUBSYS_1_IO_SIZE (20000 * 4) /* bytes */ + +#define INT_PIN_SUBSYS_0_VC8000E -1 +#define INT_PIN_SUBSYS_0_CUTREE -1 +#define INT_PIN_SUBSYS_0_DEC400 -1 +#define INT_PIN_SUBSYS_0_L2CACHE -1 +#define INT_PIN_SUBSYS_1_VC8000E -1 +#define INT_PIN_SUBSYS_1_CUTREE -1 +#define INT_PIN_SUBSYS_1_DEC400 -1 + + +/*for all subsystem, the subsys info should be listed here for subsequent use*/ +/*base_addr, iosize, resource_shared*/ +SUBSYS_CONFIG subsys_array[]= { + {SUBSYS_0_IO_ADDR, SUBSYS_0_IO_SIZE, RESOURCE_SHARED_INTER_SUBSYS}, //subsys_0 + //{SUBSYS_1_IO_ADDR, SUBSYS_1_IO_SIZE, RESOURCE_SHARED_INTER_SUBSYS}, //subsys_1 +}; + +/*here config every core in all subsystem*/ +/*NOTE: no matter what format(HEVC/H264/JPEG/AV1/...) is supported in VC8000E, just use [CORE_VC8000E] to indicate it's a VC8000E core*/ +/* CUTREE can work standalone, so it can be a subsytem or just one core of a subsytem.*/ +/*subsys_idx, core_type, offset, reg_size, irq*/ +CORE_CONFIG core_array[]= { + {0, CORE_VC8000E, 0x1000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E + //{0, CORE_MMU, 0x2000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E + //{0, CORE_AXIFE, 0x3000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_AXIFE + //{0, CORE_MMU_1, 0x4000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_VC8000E + //{0, CORE_AXIFE_1, 0x5000, 500 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_AXIFE_1 + //{0, CORE_DEC400, 0x6000, 1600 * 4, INT_PIN_SUBSYS_0_VC8000E}, //subsys_0_DEC400 + //{0, CORE_L2CACHE, 0xc000, 500 * 4, INT_PIN_SUBSYS_0_L2CACHE}, //subsys_0_l2cache + //{0, CORE_CUTREE, 0xd000, 500 * 4, INT_PIN_SUBSYS_0_L2CACHE}, //subsys_0_CUTREE + //{1, CORE_CUTREE, 0x1000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //CUTREE + //{1, CORE_MMU, 0x2000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //subsys_1_MMU + //{1, CORE_AXIFE, 0x3000, 500 * 4, INT_PIN_SUBSYS_0_CUTREE}, //subsys_1_AXIFE + +}; +/*------------------------------END-------------------------------------*/ + +/***************************TYPE AND FUNCTION DECLARATION****************/ + +/* here's all the must remember stuff */ +typedef struct +{ + SUBSYS_DATA subsys_data; //config of each core,such as base addr, iosize,etc + u32 hw_id; //VC8000E/VC8000EJ hw id to indicate project + u32 subsys_id; //subsys id for driver and sw internal use + u32 is_valid; //indicate this subsys is hantro's core or not + int pid[CORE_MAX]; //indicate which process is occupying the subsys + u32 is_reserved[CORE_MAX]; //indicate this subsys is occupied by user or not + u32 irq_received[CORE_MAX]; //indicate which core receives irq + u32 irq_status[CORE_MAX]; //IRQ status of each core + u32 job_id[CORE_MAX]; + char *buffer; + unsigned int buffsize; + volatile u8 *hwregs; + struct fasync_struct *async_queue; +} hantroenc_t; + +static int ReserveIO(void); +static void ReleaseIO(void); +//static void ResetAsic(hantroenc_t * dev); + +#ifdef hantroenc_DEBUG +static void dump_regs(unsigned long data); +#endif + +/* IRQ handler */ +#if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static irqreturn_t hantroenc_isr(int irq, void *dev_id, struct pt_regs *regs); +#else +static irqreturn_t hantroenc_isr(int irq, void *dev_id); +#endif + +/*********************local variable declaration*****************/ +unsigned long sram_base = 0; +unsigned int sram_size = 0; +/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ +static int hantroenc_major = 0; +static int total_subsys_num = 0; +static int total_core_num = 0; +volatile unsigned int asic_status = 0; +/* dynamic allocation*/ +static hantroenc_t* hantroenc_data = NULL; +#ifdef IRQ_SIMULATION +struct timer_list timer0; +struct timer_list timer1; +#endif + +/******************************************************************************/ +static int CheckEncIrq(hantroenc_t *dev,u32 *core_info,u32 *irq_status, u32 *job_id) +{ + unsigned long flags; + int rdy = 0; + u8 core_type = 0; + u8 subsys_idx = 0; + + core_type = (u8)(*core_info & 0x0F); + subsys_idx = (u8)(*core_info >> 4); + + if (subsys_idx > total_subsys_num-1) + { + *core_info = -1; + *irq_status = 0; + return 1; + } + + spin_lock_irqsave(&owner_lock, flags); + + if(dev[subsys_idx].irq_received[core_type]) + { + /* reset the wait condition(s) */ + PDEBUG("check subsys[%d][%d] irq ready\n", subsys_idx, core_type); + //dev[subsys_idx].irq_received[core_type] = 0; + rdy = 1; + *core_info = subsys_idx; + *irq_status = dev[subsys_idx].irq_status[core_type]; + if(job_id != NULL) + *job_id = dev[subsys_idx].job_id[core_type]; + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return rdy; +} +static unsigned int WaitEncReady(hantroenc_t *dev,u32 *core_info,u32 *irq_status) +{ + PDEBUG("WaitEncReady\n"); + + if(wait_event_interruptible(enc_wait_queue, CheckEncIrq(dev,core_info,irq_status, NULL))) + { + PDEBUG("ENC wait_event_interruptible interrupted\n"); + return -ERESTARTSYS; + } + + return 0; +} + +static int CheckEncIrqbyPolling(hantroenc_t *dev,u32 *core_info,u32 *irq_status,u32 *job_id) +{ + unsigned long flags; + int rdy = 0; + u8 core_type = 0; + u8 subsys_idx = 0; + u32 irq, hwId, majorId, wClr; + unsigned long reg_offset = 0; + u32 loop = 30; + u32 interval = 100; + u32 enable_status = 0; + + core_type = (u8)(*core_info & 0x0F); + subsys_idx = (u8)(*core_info >> 4); + + if (subsys_idx > total_subsys_num-1) + { + *core_info = -1; + *irq_status = 0; + return 1; + } + + do + { + spin_lock_irqsave(&owner_lock, flags); + if(dev[subsys_idx].is_reserved[core_type] == 0) + { + //printk(KERN_DEBUG"subsys[%d][%d] is not reserved\n", subsys_idx, core_type); + goto end_1; + } + else if(dev[subsys_idx].irq_received[core_type] && + (dev[subsys_idx].irq_status[core_type] & (ASIC_STATUS_FUSE_ERROR |ASIC_STATUS_HW_TIMEOUT|ASIC_STATUS_BUFF_FULL| + ASIC_STATUS_HW_RESET|ASIC_STATUS_ERROR|ASIC_STATUS_FRAME_READY )) ) + { + rdy = 1; + *core_info = subsys_idx; + *irq_status = dev[subsys_idx].irq_status[core_type]; + *job_id = dev[subsys_idx].job_id[core_type]; + goto end_1; + } + + reg_offset = dev[subsys_idx].subsys_data.core_info.offset[core_type]; + irq = (u32)ioread32((void *)(dev[subsys_idx].hwregs + reg_offset + 0x04)); + + enable_status = (u32)ioread32((void *)(dev[subsys_idx].hwregs + reg_offset + 20)); + + if(irq & ASIC_STATUS_ALL) + { + PDEBUG("check subsys[%d][%d] irq ready\n", subsys_idx, core_type); + if(irq & 0x20) + iowrite32(0, (void *)(dev[subsys_idx].hwregs + reg_offset + 0x14)); + + /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ + hwId = ioread32((void *)dev[subsys_idx].hwregs + reg_offset); + majorId = (hwId & 0x0000FF00) >> 8; + wClr = (majorId >= 0x61) ? irq: (irq & (~0x1FD)); + iowrite32(wClr, (void *)(dev[subsys_idx].hwregs + reg_offset + 0x04)); + + rdy = 1; + *core_info = subsys_idx; + *irq_status = irq; + dev[subsys_idx].irq_received[core_type] = 1; + dev[subsys_idx].irq_status[core_type] = irq; + *job_id = dev[subsys_idx].job_id[core_type]; + goto end_1; + } + + spin_unlock_irqrestore(&owner_lock, flags); + mdelay(interval); + }while(loop--); + goto end_2; + +end_1: + spin_unlock_irqrestore(&owner_lock, flags); +end_2: + return rdy; +} + +static int CheckEncAnyIrq(hantroenc_t *dev, CORE_WAIT_OUT *out) +{ + u32 i; + int rdy = 1; + u32 core_info,irq_status, job_id; + u32 core_type = CORE_VC8000E; + + for(i = 0; i < total_subsys_num; i++) + { + if(!(dev[i].subsys_data.core_info.type_info & (1<job_id[out->irq_num] = job_id; + out->irq_status[out->irq_num] = irq_status; + //printk(KERN_DEBUG "irq_status of subsys %d job_id %d is:%x\n",i,job_id,irq_status); + out->irq_num++; + rdy = 1; + } + } + + return rdy; +} + +static unsigned int WaitEncAnyReady(hantroenc_t *dev,CORE_WAIT_OUT *out) +{ + if(wait_event_interruptible(enc_wait_queue, CheckEncAnyIrq(dev,out))) + { + PDEBUG("ENC wait_event_interruptible interrupted\n"); + return -ERESTARTSYS; + } + + return 0; +} + + +static int CheckCoreOccupation(hantroenc_t *dev, u8 core_type) +{ + int ret = 0; + unsigned long flags; + + core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); + + spin_lock_irqsave(&owner_lock, flags); + if(!dev->is_reserved[core_type]) { + dev->is_reserved[core_type] = 1; +#ifndef MULTI_THR_TEST + dev->pid[core_type] = current->pid; +#endif + ret = 1; + PDEBUG("CheckCoreOccupation pid=%d\n",dev->pid[core_type]); + } + + spin_unlock_irqrestore(&owner_lock, flags); + + return ret; +} + +static int GetWorkableCore(hantroenc_t *dev,u32 *core_info,u32 *core_info_tmp) +{ + int ret = 0; + u32 i = 0; + u32 cores; + u8 core_type = 0; + u32 required_num = 0; + static u32 reserved_job_id = 0; + unsigned long flags; + /*input core_info[32 bit]: mode[1bit](1:all 0:specified)+amount[3bit](the needing amount -1)+reserved+core_type[8bit] + + output core_info[32 bit]: the reserved core info to user space and defined as below. + mode[1bit](1:all 0:specified)+amount[3bit](reserved total core num)+reserved+subsys_mapping[8bit] + */ + cores = *core_info; + required_num = ((cores >> CORE_INFO_AMOUNT_OFFSET)& 0x7)+1; + core_type = (u8)(cores&0xFF); + + if (*core_info_tmp == 0) + *core_info_tmp = required_num << CORE_INFO_AMOUNT_OFFSET; + else + required_num = (*core_info_tmp >> CORE_INFO_AMOUNT_OFFSET); + + PDEBUG("GetWorkableCore:required_num=%d,core_info=%x\n",required_num,*core_info); + + if(required_num) + { + /* a valid free Core with specified core type */ + for (i = 0; i < total_subsys_num; i++) + { + if (dev[i].subsys_data.core_info.type_info & (1 << core_type)) + { + core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); + if(dev[i].is_valid && CheckCoreOccupation(&dev[i], core_type)) + { + *core_info_tmp = ((((*core_info_tmp >> CORE_INFO_AMOUNT_OFFSET)-1)<> CORE_INFO_AMOUNT_OFFSET)==0) + { + ret = 1; + spin_lock_irqsave(&owner_lock, flags); + *core_info = (reserved_job_id << 16)|(*core_info_tmp & 0xFF); + dev[i].job_id[core_type] = reserved_job_id; + reserved_job_id++; + spin_unlock_irqrestore(&owner_lock, flags); + *core_info_tmp = 0; + required_num = 0; + break; + } + } + } + } + } + else + ret = 1; + + PDEBUG("*core_info = %x\n",*core_info); + return ret; +} + +static long ReserveEncoder(hantroenc_t *dev,u32 *core_info) +{ + u32 core_info_tmp = 0; +#ifdef MULTI_THR_TEST + struct wait_list_node *wait_node; + u32 start_id=0; +#endif + + + /*If HW resources are shared inter cores, just make sure only one is using the HW*/ + if (dev[0].subsys_data.cfg.resouce_shared) + { + if (down_interruptible(&enc_core_sem)) + return -ERESTARTSYS; + } + +#ifdef MULTI_THR_TEST + while(1) + { + start_id=request_wait_node(&wait_node,start_id); + if(wait_node->sem_used==1) + { + if(GetWorkableCore(dev,core_info,&core_info_tmp)) + { + down_interruptible(&wait_node->wait_sem); + wait_node->sem_used=0; + wait_node->used_flag=0; + break; + } + else + { + start_id++; + } + } + else + { + wait_node->wait_cond = *core_info; + list_add_tail(&wait_node->wait_list,&reserve_header); + down_interruptible(&wait_node->wait_sem); + *core_info = wait_node->wait_cond; + list_del(&wait_node->wait_list); + wait_node->sem_used=0; + wait_node->used_flag=0; + break; + } + } +#else + + /* lock a core that has specified core id*/ + if(wait_event_interruptible(hw_queue, + GetWorkableCore(dev,core_info,&core_info_tmp) != 0 )) + return -ERESTARTSYS; +#endif + return 0; +} + +static void ReleaseEncoder(hantroenc_t * dev,u32 *core_info) +{ + unsigned long flags; + u8 core_type = 0, subsys_idx = 0, unCheckPid = 0; + unCheckPid = (u8)((*core_info) >> 31); +#ifdef MULTI_THR_TEST + u32 release_ok=0; + struct list_head *node; + struct wait_list_node *wait_node; + u32 core_info_tmp = 0; +#endif + subsys_idx = (u8)((*core_info&0xF0) >> 4); + core_type = (u8)(*core_info&0x0F); + + PDEBUG("ReleaseEncoder:subsys_idx=%d,core_type=%x\n",subsys_idx,core_type); + /* release specified subsys and core type */ + + if (dev[subsys_idx].subsys_data.core_info.type_info & (1 << core_type)) + { + core_type = (core_type == CORE_VC8000EJ ? CORE_VC8000E : core_type); + spin_lock_irqsave(&owner_lock, flags); + PDEBUG("subsys[%d].pid[%d]=%d,current->pid=%d\n",subsys_idx, core_type, dev[subsys_idx].pid[core_type],current->pid); +#ifdef MULTI_THR_TEST + if(dev[subsys_idx].is_reserved[core_type]) +#else + if(dev[subsys_idx].is_reserved[core_type] && (dev[subsys_idx].pid[core_type] == current->pid || unCheckPid == 1)) +#endif + { + dev[subsys_idx].pid[core_type] = -1; + dev[subsys_idx].is_reserved[core_type] = 0; + dev[subsys_idx].irq_received[core_type] = 0; + dev[subsys_idx].irq_status[core_type] = 0; + dev[subsys_idx].job_id[core_type] = 0; + spin_unlock_irqrestore(&owner_lock, flags); +#ifdef MULTI_THR_TEST + release_ok=0; + if(list_empty(&reserve_header)) + { + request_wait_sema(&wait_node); + up(&wait_node->wait_sem); + } + else + { + list_for_each(node,&reserve_header) + { + wait_node = container_of(node,struct wait_list_node,wait_list); + if((GetWorkableCore(dev,&wait_node->wait_cond,&core_info_tmp))&&(wait_node->sem_used==0)) + { + release_ok =1; + wait_node->sem_used = 1; + up(&wait_node->wait_sem); + break; + } + } + if(release_ok==0) + { + request_wait_sema(&wait_node); + up(&wait_node->wait_sem); + } + } +#endif + + } + else + { + if (dev[subsys_idx].pid[core_type] != current->pid && unCheckPid == 0) + printk(KERN_ERR "WARNING:pid(%d) is trying to release core reserved by pid(%d)\n",current->pid,dev[subsys_idx].pid[core_type]); + spin_unlock_irqrestore(&owner_lock, flags); + } + //wake_up_interruptible_all(&hw_queue); + } +#ifndef MULTI_THR_TEST + wake_up_interruptible_all(&hw_queue); +#endif + if(dev->subsys_data.cfg.resouce_shared) + up(&enc_core_sem); + + return; +} + +#ifdef IRQ_SIMULATION +static void get_random_bytes(void *buf, int nbytes); + +static void hantroenc_trigger_irq_0(unsigned long value) +{ + PDEBUG("trigger core 0 irq\n"); + del_timer(&timer0); + hantroenc_isr(0,(void *)&hantroenc_data[0]); +} +static void hantroenc_trigger_irq_1(unsigned long value) +{ + PDEBUG("trigger core 1 irq\n"); + del_timer(&timer1); + hantroenc_isr(0,(void *)&hantroenc_data[1]); +} + +#endif + +static long hantroenc_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + unsigned int tmp; +#ifdef HANTROMMU_SUPPORT + u32 i = 0; + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; +#endif + + PDEBUG("ioctl cmd 0x%08ux\n", cmd); + /* + * extract the type and number bitfields, and don't encode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if(_IOC_TYPE(cmd) != HANTRO_IOC_MAGIC +#ifdef HANTROMMU_SUPPORT + &&_IOC_TYPE(cmd) != HANTRO_IOC_MMU +#endif + ) + return -ENOTTY; + if((_IOC_TYPE(cmd) == HANTRO_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_IOC_MAXNR) +#ifdef HANTROMMU_SUPPORT + ||(_IOC_TYPE(cmd) == HANTRO_IOC_MMU && + _IOC_NR(cmd) > HANTRO_IOC_MMU_MAXNR) +#endif + ) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if(_IOC_DIR(cmd) & _IOC_READ) +#if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); +#endif + else if(_IOC_DIR(cmd) & _IOC_WRITE) +#if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); +#endif + if(err) + return -EFAULT; + + switch (cmd) + { + case HANTRO_IOCH_GET_VCMD_ENABLE: + { + __put_user(0, (unsigned long *) arg); + break; + } + + case HANTRO_IOCG_HWOFFSET: + { + u32 id; + __get_user(id, (u32*)arg); + + if(id >= total_subsys_num) + { + return -EFAULT; + } + __put_user(hantroenc_data[id].subsys_data.cfg.base_addr, (unsigned long *) arg); + break; + } + + case HANTRO_IOCG_HWIOSIZE: + { + u32 id; + u32 io_size; + __get_user(id, (u32*)arg); + + if(id >= total_subsys_num) + { + return -EFAULT; + } + io_size = hantroenc_data[id].subsys_data.cfg.iosize; + __put_user(io_size, (u32 *) arg); + + return 0; + } + case HANTRO_IOCG_SRAMOFFSET: + __put_user(sram_base, (unsigned long *) arg); + break; + case HANTRO_IOCG_SRAMEIOSIZE: + __put_user(sram_size, (unsigned int *) arg); + break; + case HANTRO_IOCG_CORE_NUM: + __put_user(total_subsys_num, (unsigned int *) arg); + break; + case HANTRO_IOCG_CORE_INFO: + { + u32 idx; + SUBSYS_CORE_INFO in_data; + copy_from_user(&in_data, (void*)arg, sizeof(SUBSYS_CORE_INFO)); + idx = in_data.type_info; + if (idx > total_subsys_num - 1) + return -1; + + copy_to_user((void*)arg, &hantroenc_data[idx].subsys_data.core_info, sizeof(SUBSYS_CORE_INFO)); + break; + } + case HANTRO_IOCH_ENC_RESERVE: + { + u32 core_info; + int ret; + PDEBUG("Reserve ENC Cores\n"); + __get_user(core_info, (u32*)arg); + ret = ReserveEncoder(hantroenc_data,&core_info); + if (ret == 0) + __put_user(core_info, (u32 *) arg); + return ret; + } + case HANTRO_IOCH_ENC_RELEASE: + { + u32 core_info; + __get_user(core_info, (u32*)arg); + + PDEBUG("Release ENC Core\n"); + + ReleaseEncoder(hantroenc_data,&core_info); + + break; + } + + case HANTRO_IOCG_CORE_WAIT: + { + u32 core_info; + u32 irq_status; + __get_user(core_info, (u32*)arg); + #ifdef IRQ_SIMULATION + u32 random_num; + get_random_bytes(&random_num, sizeof(u32)); + random_num = random_num%10+80; + PDEBUG("random_num=%d\n",random_num); + + /*init a timer to trigger irq*/ + if (core_info==1) + { + init_timer(&timer0); + timer0.function = &hantroenc_trigger_irq_0; + timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer0); + } + + if (core_info==2) + { + init_timer(&timer1); + timer1.function = &hantroenc_trigger_irq_1; + timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer1); + } + #endif + + tmp = WaitEncReady(hantroenc_data,&core_info,&irq_status); + if (tmp==0) + { + __put_user(irq_status, (unsigned int *)arg); + return core_info;//return core_id + } + else + { + return -1; + } + + break; + } + case HANTRO_IOCG_ANYCORE_WAIT: + { + CORE_WAIT_OUT out; + memset(&out, 0, sizeof(CORE_WAIT_OUT)); +#ifdef IRQ_SIMULATION + u32 random_num; + get_random_bytes(&random_num, sizeof(u32)); + random_num = random_num%10+80; + PDEBUG("random_num=%d\n",random_num); + + /*init a timer to trigger irq*/ + if (core_info==1) + { + init_timer(&timer0); + timer0.function = &hantroenc_trigger_irq_0; + timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer0); + } + + if (core_info==2) + { + init_timer(&timer1); + timer1.function = &hantroenc_trigger_irq_1; + timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer1); + } +#endif + + tmp = WaitEncAnyReady(hantroenc_data,&out); + if (tmp==0) + { + copy_to_user((void*)arg, &out, sizeof(CORE_WAIT_OUT)); + return 0; + } + else + { + return -1; + } + + break; + } + + default: + { +#ifdef HANTROMMU_SUPPORT + if(_IOC_TYPE(cmd) == HANTRO_IOC_MMU) + { + memset (mmu_hwregs, 0, MAX_SUBSYS_NUM*2*sizeof(u8*)); + for (i = 0; i < total_subsys_num; i++ ) + { + if(hantroenc_data[i].subsys_data.core_info.type_info & (1<private_data = (void *) dev; + + PDEBUG("dev opened\n"); + return result; +} +static int hantroenc_release(struct inode *inode, struct file *filp) +{ + hantroenc_t *dev = (hantroenc_t *) filp->private_data; + u32 core_id = 0, i = 0; + +#ifdef hantroenc_DEBUG + dump_regs((unsigned long) dev); /* dump the regs */ +#endif + unsigned long flags; + + PDEBUG("dev closed\n"); + + for (i = 0;i < total_subsys_num; i++) + { + for (core_id = 0; core_id < CORE_MAX; core_id++) + { + spin_lock_irqsave(&owner_lock, flags); + if (dev[i].is_reserved[core_id] == 1 && dev[i].pid[core_id] == current->pid) + { + dev[i].pid[core_id] = -1; + dev[i].is_reserved[core_id] = 0; + dev[i].irq_received[core_id] = 0; + dev[i].irq_status[core_id] = 0; + PDEBUG("release reserved core\n"); + } + spin_unlock_irqrestore(&owner_lock, flags); + } + } + +#ifdef HANTROMMU_SUPPORT + for(i = 0; i < total_subsys_num; i++) + { + if(!(hantroenc_data[i].subsys_data.core_info.type_info & (1<subsys_data.cfg.resouce_shared) + up(&enc_core_sem); + + return 0; +} + +/* VFS methods */ +static struct file_operations hantroenc_fops = { + .owner= THIS_MODULE, + .open = hantroenc_open, + .release = hantroenc_release, + .unlocked_ioctl = hantroenc_ioctl, + .fasync = NULL, +}; + + +/*----------------------------------------- + platform register +-----------------------------------------*/ + +static const struct of_device_id isp_of_match[] = { + { .compatible = "thead,light-vc8000e", }, + { /* sentinel */ }, +}; + + +static int encoder_hantrodec_probe(struct platform_device *pdev) +{ + int result; + int i, j; + + struct resource *mem; + mem = platform_get_resource(pdev,IORESOURCE_MEM,0); + if(mem->start) + subsys_array[0].base_addr = mem->start; + core_array[0].irq = platform_get_irq(pdev,0); + + total_subsys_num = sizeof(subsys_array)/sizeof(SUBSYS_CONFIG); + + for (i = 0; i< total_subsys_num; i++) + { + printk(KERN_INFO "hantroenc: module init - subsys[%d] addr =%p\n",i, + (void *)subsys_array[i].base_addr); + } + + hantroenc_data = (hantroenc_t *)vmalloc(sizeof(hantroenc_t)*total_subsys_num); + if (hantroenc_data == NULL) + goto err1; + memset(hantroenc_data,0,sizeof(hantroenc_t)*total_subsys_num); + + for(i = 0; i < total_subsys_num; i++) + { + hantroenc_data[i].subsys_data.cfg = subsys_array[i]; + hantroenc_data[i].async_queue = NULL; + hantroenc_data[i].hwregs = NULL; + hantroenc_data[i].subsys_id = i; + for(j = 0; j < CORE_MAX; j++) + hantroenc_data[i].subsys_data.core_info.irq[j] = -1; + } + + total_core_num = sizeof(core_array)/sizeof(CORE_CONFIG); + for (i = 0; i < total_core_num; i++) + { + hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.type_info |= (1<<(core_array[i].core_type)); + hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.offset[core_array[i].core_type] = core_array[i].offset; + hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.regSize[core_array[i].core_type] = core_array[i].reg_size; + hantroenc_data[core_array[i].subsys_idx].subsys_data.core_info.irq[core_array[i].core_type] = core_array[i].irq; + } + + result = register_chrdev(hantroenc_major, "vc8000", &hantroenc_fops); + if(result < 0) + { + printk(KERN_INFO "hantroenc: unable to get major <%d>\n", + hantroenc_major); + goto err1; + } + else if(result != 0) /* this is for dynamic major */ + { + hantroenc_major = result; + } + + result = ReserveIO(); + if(result < 0) + { + goto err; + } + + //ResetAsic(hantroenc_data); /* reset hardware */ + + sema_init(&enc_core_sem, 1); + +#ifdef HANTROMMU_SUPPORT + /* MMU only initial once No matter how many MMU we have */ + for(i = 0; i < total_subsys_num; i++) + { + if(!(hantroenc_data[i].subsys_data.core_info.type_info & (1< busy, change your config\n", + hantroenc_data[i].subsys_data.core_info.irq[j]); + ReleaseIO(); + goto err; + } + } + else + { + printk(KERN_INFO "hantroenc: IRQ not in use!\n"); + } + } + } +#ifdef MULTI_THR_TEST + init_reserve_wait(total_subsys_num); +#endif + printk(KERN_INFO "hantroenc: module inserted. Major <%d>\n", hantroenc_major); + + return 0; + + err: + unregister_chrdev(hantroenc_major, "vc8000"); + err1: + if (hantroenc_data != NULL) + vfree(hantroenc_data); + printk(KERN_INFO "hantroenc: module not inserted\n"); + return result; +} + +static int encoder_hantrodec_remove(struct platform_device *pdev) +{ + int i=0, j = 0; +#ifdef HANTROMMU_SUPPORT + volatile u8* mmu_hwregs[MAX_SUBSYS_NUM][2]; +#endif + + for(i=0;i> 16) & 0xFFFF) != ((ENC_HW_ID1 >> 16) & 0xFFFF))) && + ((((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID2 >> 16) & 0xFFFF)))) + { + printk(KERN_INFO "hantroenc: HW not found at %p\n", + (void *)hantroenc_data[i].subsys_data.cfg.base_addr); + #ifdef hantroenc_DEBUG + dump_regs((unsigned long) &hantroenc_data); + #endif + hantroenc_data[i].is_valid = 0; + ReleaseIO(); + continue; + } + hantroenc_data[i].hw_id = hwid; + hantroenc_data[i].is_valid = 1; + found_hw = 1; + + hw_cfg = (u32)ioread32((void *)hantroenc_data[i].hwregs + hantroenc_data[i].subsys_data.core_info.offset[VC8000E_core_idx] + 320); + hantroenc_data[i].subsys_data.core_info.type_info &= 0xFFFFFFFC; + if(hw_cfg & 0x88000000) + hantroenc_data[i].subsys_data.core_info.type_info |= (1< with ID <0x%08x>\n", + (void *)hantroenc_data[i].subsys_data.cfg.base_addr, hwid); + + } + + if (found_hw == 0) + { + printk(KERN_ERR "hantroenc: NO ANY HW found!!\n"); + return -1; + } + + return 0; +} + +static void ReleaseIO(void) +{ + u32 i; + for (i=0;i<=total_subsys_num;i++) + { + if (hantroenc_data[i].is_valid == 0) + continue; + if(hantroenc_data[i].hwregs) + iounmap((void *) hantroenc_data[i].hwregs); + release_mem_region(hantroenc_data[i].subsys_data.cfg.base_addr, hantroenc_data[i].subsys_data.cfg.iosize); + } +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +irqreturn_t hantroenc_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +irqreturn_t hantroenc_isr(int irq, void *dev_id) +#endif +{ + unsigned int handled = 0; + hantroenc_t *dev = (hantroenc_t *) dev_id; + u32 irq_status; + unsigned long flags; + u32 core_type = 0, i = 0; + unsigned long reg_offset = 0; + u32 hwId, majorId, wClr; + + /*get core id by irq from subsys config*/ + for (i = 0; i < CORE_MAX; i++) + { + if (dev->subsys_data.core_info.irq[i] == irq) + { + core_type = i; + reg_offset = dev->subsys_data.core_info.offset[i]; + break; + } + } + + /*If core is not reserved by any user, but irq is received, just clean it*/ + spin_lock_irqsave(&owner_lock, flags); + if (!dev->is_reserved[core_type]) + { + printk(KERN_DEBUG "hantroenc_isr:received IRQ but core is not reserved!\n"); + irq_status = (u32)ioread32((void *)(dev->hwregs + reg_offset + 0x04)); + if(irq_status & 0x01) + { + /* Disable HW when buffer over-flow happen + * HW behavior changed in over-flow + * in-pass, HW cleanup HWIF_ENC_E auto + * new version: ask SW cleanup HWIF_ENC_E when buffer over-flow + */ + if(irq_status & 0x20) + iowrite32(0, (void *)(dev->hwregs + reg_offset + 0x14)); + + /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ + hwId = ioread32((void *)dev->hwregs + reg_offset); + majorId = (hwId & 0x0000FF00) >> 8; + wClr = (majorId >= 0x61) ? irq_status: (irq_status & (~0x1FD)); + iowrite32(wClr, (void *)(dev->hwregs + reg_offset + 0x04)); + } + spin_unlock_irqrestore(&owner_lock, flags); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&owner_lock, flags); + + printk(KERN_DEBUG "hantroenc_isr:received IRQ!\n"); + irq_status = (u32)ioread32((void *)(dev->hwregs + reg_offset + 0x04)); + printk(KERN_DEBUG "irq_status of subsys %d core %d is:%x\n",dev->subsys_id,core_type,irq_status); + if(irq_status & 0x01) + { + /* Disable HW when buffer over-flow happen + * HW behavior changed in over-flow + * in-pass, HW cleanup HWIF_ENC_E auto + * new version: ask SW cleanup HWIF_ENC_E when buffer over-flow + */ + if(irq_status & 0x20) + iowrite32(0, (void *)(dev->hwregs + reg_offset + 0x14)); + + /* clear all IRQ bits. (hwId >= 0x80006100) means IRQ is cleared by writting 1 */ + hwId = ioread32((void *)dev->hwregs + reg_offset); + majorId = (hwId & 0x0000FF00) >> 8; + wClr = (majorId >= 0x61) ? irq_status: (irq_status & (~0x1FD)); + iowrite32(wClr, (void *)(dev->hwregs + reg_offset + 0x04)); + + spin_lock_irqsave(&owner_lock, flags); + dev->irq_received[core_type] = 1; + dev->irq_status[core_type] = irq_status & (~0x01); + spin_unlock_irqrestore(&owner_lock, flags); + + wake_up_interruptible_all(&enc_wait_queue); + handled++; + } + if(!handled) + { + PDEBUG("IRQ received, but not hantro's!\n"); + } + return IRQ_HANDLED; +} + +#ifdef hantroenc_DEBUG +static void ResetAsic(hantroenc_t * dev) +{ + int i,n; + for (n=0;niosize; i += 4) + { + PDEBUG("\toffset %02X = %08X\n", i, ioread32(dev->hwregs + i)); + } + PDEBUG("Reg Dump End\n"); +} +#endif + + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_vcmd_driver.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_vcmd_driver.c new file mode 100644 index 000000000..9d433d853 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vc8000_vcmd_driver.c @@ -0,0 +1,4685 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +#include +#include +/* needed for __init,__exit directives */ +#include +/* needed for remap_page_range + SetPageReserved + ClearPageReserved +*/ +#include +/* obviously, for kmalloc */ +#include +/* for struct file_operations, register_chrdev() */ +#include +/* standard error codes */ +#include + +#include +#include +#include +#include + + +#include +/* request_irq(), free_irq() */ +#include +#include + +#include +#include +/* needed for virt_to_phys() */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* our own stuff */ +#include +#include +#include "vcmdswhwregisters.h" +#include "bidirect_list.h" +#include "vc8000_driver.h" + +/*------------------------------------------------------------------------ +*****************************VCMD CONFIGURATION BY CUSTOMER******************************** +-------------------------------------------------------------------------*/ +//video encoder vcmd configuration + +#define VCMD_ENC_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_0 -1 +#define VCMD_ENC_MODULE_TYPE_0 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_0 0X2000 //0X6000 /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_ENC_MODULE_MMU0_ADDR_0 0X4000 //0X2000 +#define VCMD_ENC_MODULE_MMU1_ADDR_0 0XFFFF //0X4000 +#define VCMD_ENC_MODULE_AXIFE0_ADDR_0 0XFFFF //0X3000 +#define VCMD_ENC_MODULE_AXIFE1_ADDR_0 0XFFFF //0X5000 + +#define VCMD_ENC_IO_ADDR_1 0x91000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_1 -1 +#define VCMD_ENC_MODULE_TYPE_1 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_ENC_IO_ADDR_2 0x92000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_2 -1 +#define VCMD_ENC_MODULE_TYPE_2 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_ENC_IO_ADDR_3 0x93000 /*customer specify according to own platform*/ +#define VCMD_ENC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_ENC_INT_PIN_3 -1 +#define VCMD_ENC_MODULE_TYPE_3 0 +#define VCMD_ENC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_ENC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_ENC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_ENC_MODULE_MMU_ADDR_3 0XFFFF + +//video encoder cutree/IM vcmd configuration + + +#define VCMD_IM_IO_ADDR_0 0x94000 //0xA0000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_0 -1 +#define VCMD_IM_MODULE_TYPE_0 1 +#define VCMD_IM_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_IM_MODULE_MMU0_ADDR_0 0XFFFF //0X2000 +#define VCMD_IM_MODULE_MMU1_ADDR_0 0XFFFF +#define VCMD_IM_MODULE_AXIFE0_ADDR_0 0XFFFF //0X3000 +#define VCMD_IM_MODULE_AXIFE1_ADDR_0 0XFFFF //0XFFFF + +#define VCMD_IM_IO_ADDR_1 0xa1000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_1 -1 +#define VCMD_IM_MODULE_TYPE_1 1 +#define VCMD_IM_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_IM_IO_ADDR_2 0xa2000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_2 -1 +#define VCMD_IM_MODULE_TYPE_2 1 +#define VCMD_IM_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_IM_IO_ADDR_3 0xa3000 /*customer specify according to own platform*/ +#define VCMD_IM_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_IM_INT_PIN_3 -1 +#define VCMD_IM_MODULE_TYPE_3 1 +#define VCMD_IM_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_IM_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_IM_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_IM_MODULE_MMU_ADDR_3 0XFFFF + +//video decoder vcmd configuration + + +#define VCMD_DEC_IO_ADDR_0 0xb0000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_0 -1 +#define VCMD_DEC_MODULE_TYPE_0 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_0 0XFFFF + + +#define VCMD_DEC_IO_ADDR_1 0xb1000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_1 -1 +#define VCMD_DEC_MODULE_TYPE_1 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_DEC_IO_ADDR_2 0xb2000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_2 -1 +#define VCMD_DEC_MODULE_TYPE_2 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_DEC_IO_ADDR_3 0xb3000 /*customer specify according to own platform*/ +#define VCMD_DEC_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_DEC_INT_PIN_3 -1 +#define VCMD_DEC_MODULE_TYPE_3 2 +#define VCMD_DEC_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_DEC_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_DEC_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_DEC_MODULE_MMU_ADDR_3 0XFFFF + +//JPEG encoder vcmd configuration + +#define VCMD_JPEGE_IO_ADDR_0 0x90000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_0 -1 +#define VCMD_JPEGE_MODULE_TYPE_0 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_0 0x1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_0 0XFFFF //0X4000 /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGE_MODULE_MMU0_ADDR_0 0XFFFF //0X2000 +#define VCMD_JPEGE_MODULE_MMU1_ADDR_0 0XFFFF +#define VCMD_JPEGE_MODULE_AXIFE0_ADDR_0 0XFFFF //0X3000 +#define VCMD_JPEGE_MODULE_AXIFE1_ADDR_0 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_1 0xC1000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_1 -1 +#define VCMD_JPEGE_MODULE_TYPE_1 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_2 0xC2000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_2 -1 +#define VCMD_JPEGE_MODULE_TYPE_2 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGE_IO_ADDR_3 0xC3000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGE_INT_PIN_3 -1 +#define VCMD_JPEGE_MODULE_TYPE_3 3 +#define VCMD_JPEGE_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGE_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGE_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGE_MODULE_MMU_ADDR_3 0XFFFF + + +//JPEG decoder vcmd configuration + +#define VCMD_JPEGD_IO_ADDR_0 0xD0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_0 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_0 -1 +#define VCMD_JPEGD_MODULE_TYPE_0 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_0 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_0 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_0 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_0 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_1 0xD1000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_1 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_1 -1 +#define VCMD_JPEGD_MODULE_TYPE_1 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_1 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_1 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_1 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_1 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_2 0xD2000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_2 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_2 -1 +#define VCMD_JPEGD_MODULE_TYPE_2 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_2 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_2 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_2 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_2 0XFFFF + +#define VCMD_JPEGD_IO_ADDR_3 0xD3000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_IO_SIZE_3 (ASIC_VCMD_SWREG_AMOUNT * 4) /* bytes */ +#define VCMD_JPEGD_INT_PIN_3 -1 +#define VCMD_JPEGD_MODULE_TYPE_3 4 +#define VCMD_JPEGD_MODULE_MAIN_ADDR_3 0x0000 /*customer specify according to own platform*/ +#define VCMD_JPEGD_MODULE_DEC400_ADDR_3 0XFFFF /*0xffff means no such kind of submodule*/ +#define VCMD_JPEGD_MODULE_L2CACHE_ADDR_3 0XFFFF +#define VCMD_JPEGD_MODULE_MMU_ADDR_3 0XFFFF + +struct vcmd_config +{ + unsigned long vcmd_base_addr; + u32 vcmd_iosize; + int vcmd_irq; + u32 sub_module_type; /*input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4*/ + u16 submodule_main_addr; // in byte + u16 submodule_dec400_addr;//if submodule addr == 0xffff, this submodule does not exist.// in byte + u16 submodule_L2Cache_addr; // in byte + u16 submodule_MMU_addr[2]; // in byte + u16 submodule_axife_addr[2]; // in byte +}; + +#define NETINT +//#define MAGVII +//#define OYB_VCEJ +//#define OYB_VCE + +/*for all vcmds, the core info should be listed here for subsequent use*/ +static struct vcmd_config vcmd_core_array[]= { +#if defined(NETINT) || defined(OYB_VCE) + //encoder configuration + {VCMD_ENC_IO_ADDR_0, + VCMD_ENC_IO_SIZE_0, + VCMD_ENC_INT_PIN_0, + VCMD_ENC_MODULE_TYPE_0, + VCMD_ENC_MODULE_MAIN_ADDR_0, + VCMD_ENC_MODULE_DEC400_ADDR_0, + VCMD_ENC_MODULE_L2CACHE_ADDR_0, + {VCMD_ENC_MODULE_MMU0_ADDR_0, + VCMD_ENC_MODULE_MMU1_ADDR_0}, + {VCMD_ENC_MODULE_AXIFE0_ADDR_0, + VCMD_ENC_MODULE_AXIFE1_ADDR_0}}, +#endif +#if 0 + {VCMD_ENC_IO_ADDR_1, + VCMD_ENC_IO_SIZE_1, + VCMD_ENC_INT_PIN_1, + VCMD_ENC_MODULE_TYPE_1, + VCMD_ENC_MODULE_MAIN_ADDR_1, + VCMD_ENC_MODULE_DEC400_ADDR_1, + VCMD_ENC_MODULE_L2CACHE_ADDR_1, + VCMD_ENC_MODULE_MMU_ADDR_1}, + + {VCMD_ENC_IO_ADDR_2, + VCMD_ENC_IO_SIZE_2, + VCMD_ENC_INT_PIN_2, + VCMD_ENC_MODULE_TYPE_2, + VCMD_ENC_MODULE_MAIN_ADDR_2, + VCMD_ENC_MODULE_DEC400_ADDR_2, + VCMD_ENC_MODULE_L2CACHE_ADDR_2, + VCMD_ENC_MODULE_MMU_ADDR_2}, + + {VCMD_ENC_IO_ADDR_3, + VCMD_ENC_IO_SIZE_3, + VCMD_ENC_INT_PIN_3, + VCMD_ENC_MODULE_TYPE_3, + VCMD_ENC_MODULE_MAIN_ADDR_3, + VCMD_ENC_MODULE_DEC400_ADDR_3, + VCMD_ENC_MODULE_L2CACHE_ADDR_3, + VCMD_ENC_MODULE_MMU_ADDR_3}, +#endif + //cutree/IM configuration +#if defined(NETINT) || defined(OYB_VCE) + {VCMD_IM_IO_ADDR_0, + VCMD_IM_IO_SIZE_0, + VCMD_IM_INT_PIN_0, + VCMD_IM_MODULE_TYPE_0, + VCMD_IM_MODULE_MAIN_ADDR_0, + VCMD_IM_MODULE_DEC400_ADDR_0, + VCMD_IM_MODULE_L2CACHE_ADDR_0, + {VCMD_IM_MODULE_MMU0_ADDR_0, + VCMD_IM_MODULE_MMU1_ADDR_0}, + {VCMD_IM_MODULE_AXIFE0_ADDR_0, + VCMD_IM_MODULE_AXIFE1_ADDR_0}}, +#endif +#if 0 + {VCMD_IM_IO_ADDR_1, + VCMD_IM_IO_SIZE_1, + VCMD_IM_INT_PIN_1, + VCMD_IM_MODULE_TYPE_1, + VCMD_IM_MODULE_MAIN_ADDR_1, + VCMD_IM_MODULE_DEC400_ADDR_1, + VCMD_IM_MODULE_L2CACHE_ADDR_1, + VCMD_IM_MODULE_MMU_ADDR_1}, + + {VCMD_IM_IO_ADDR_2, + VCMD_IM_IO_SIZE_2, + VCMD_IM_INT_PIN_2, + VCMD_IM_MODULE_TYPE_2, + VCMD_IM_MODULE_MAIN_ADDR_2, + VCMD_IM_MODULE_DEC400_ADDR_2, + VCMD_IM_MODULE_L2CACHE_ADDR_2, + VCMD_IM_MODULE_MMU_ADDR_2}, + + {VCMD_IM_IO_ADDR_3, + VCMD_IM_IO_SIZE_3, + VCMD_IM_INT_PIN_3, + VCMD_IM_MODULE_TYPE_3, + VCMD_IM_MODULE_MAIN_ADDR_3, + VCMD_IM_MODULE_DEC400_ADDR_3, + VCMD_IM_MODULE_L2CACHE_ADDR_3, + VCMD_IM_MODULE_MMU_ADDR_3}, + + //decoder configuration + {VCMD_DEC_IO_ADDR_0, + VCMD_DEC_IO_SIZE_0, + VCMD_DEC_INT_PIN_0, + VCMD_DEC_MODULE_TYPE_0, + VCMD_DEC_MODULE_MAIN_ADDR_0, + VCMD_DEC_MODULE_DEC400_ADDR_0, + VCMD_DEC_MODULE_L2CACHE_ADDR_0, + VCMD_DEC_MODULE_MMU_ADDR_0}, + + {VCMD_DEC_IO_ADDR_1, + VCMD_DEC_IO_SIZE_1, + VCMD_DEC_INT_PIN_1, + VCMD_DEC_MODULE_TYPE_1, + VCMD_DEC_MODULE_MAIN_ADDR_1, + VCMD_DEC_MODULE_DEC400_ADDR_1, + VCMD_DEC_MODULE_L2CACHE_ADDR_1, + VCMD_DEC_MODULE_MMU_ADDR_1}, + + {VCMD_DEC_IO_ADDR_2, + VCMD_DEC_IO_SIZE_2, + VCMD_DEC_INT_PIN_2, + VCMD_DEC_MODULE_TYPE_2, + VCMD_DEC_MODULE_MAIN_ADDR_2, + VCMD_DEC_MODULE_DEC400_ADDR_2, + VCMD_DEC_MODULE_L2CACHE_ADDR_2, + VCMD_DEC_MODULE_MMU_ADDR_2}, + + {VCMD_DEC_IO_ADDR_3, + VCMD_DEC_IO_SIZE_3, + VCMD_DEC_INT_PIN_3, + VCMD_DEC_MODULE_TYPE_3, + VCMD_DEC_MODULE_MAIN_ADDR_3, + VCMD_DEC_MODULE_DEC400_ADDR_3, + VCMD_DEC_MODULE_L2CACHE_ADDR_3, + VCMD_DEC_MODULE_MMU_ADDR_3}, +#endif +#if defined(MAGVII) || defined(OYB_VCEJ) + //JPEG encoder configuration + {VCMD_JPEGE_IO_ADDR_0, + VCMD_JPEGE_IO_SIZE_0, + VCMD_JPEGE_INT_PIN_0, + VCMD_JPEGE_MODULE_TYPE_0, + VCMD_JPEGE_MODULE_MAIN_ADDR_0, + VCMD_JPEGE_MODULE_DEC400_ADDR_0, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_0, + {VCMD_JPEGE_MODULE_MMU0_ADDR_0, + VCMD_JPEGE_MODULE_MMU1_ADDR_0}, + {VCMD_JPEGE_MODULE_AXIFE0_ADDR_0, + VCMD_JPEGE_MODULE_AXIFE1_ADDR_0}}, +#endif +#if 0 + {VCMD_JPEGE_IO_ADDR_1, + VCMD_JPEGE_IO_SIZE_1, + VCMD_JPEGE_INT_PIN_1, + VCMD_JPEGE_MODULE_TYPE_1, + VCMD_JPEGE_MODULE_MAIN_ADDR_1, + VCMD_JPEGE_MODULE_DEC400_ADDR_1, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGE_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGE_IO_ADDR_2, + VCMD_JPEGE_IO_SIZE_2, + VCMD_JPEGE_INT_PIN_2, + VCMD_JPEGE_MODULE_TYPE_2, + VCMD_JPEGE_MODULE_MAIN_ADDR_2, + VCMD_JPEGE_MODULE_DEC400_ADDR_2, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGE_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGE_IO_ADDR_3, + VCMD_JPEGE_IO_SIZE_3, + VCMD_JPEGE_INT_PIN_3, + VCMD_JPEGE_MODULE_TYPE_3, + VCMD_JPEGE_MODULE_MAIN_ADDR_3, + VCMD_JPEGE_MODULE_DEC400_ADDR_3, + VCMD_JPEGE_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGE_MODULE_MMU_ADDR_3}, + //JPEG decoder configuration + {VCMD_JPEGD_IO_ADDR_0, + VCMD_JPEGD_IO_SIZE_0, + VCMD_JPEGD_INT_PIN_0, + VCMD_JPEGD_MODULE_TYPE_0, + VCMD_JPEGD_MODULE_MAIN_ADDR_0, + VCMD_JPEGD_MODULE_DEC400_ADDR_0, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_0, + VCMD_JPEGD_MODULE_MMU_ADDR_0}, + + {VCMD_JPEGD_IO_ADDR_1, + VCMD_JPEGD_IO_SIZE_1, + VCMD_JPEGD_INT_PIN_1, + VCMD_JPEGD_MODULE_TYPE_1, + VCMD_JPEGD_MODULE_MAIN_ADDR_1, + VCMD_JPEGD_MODULE_DEC400_ADDR_1, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_1, + VCMD_JPEGD_MODULE_MMU_ADDR_1}, + + {VCMD_JPEGD_IO_ADDR_2, + VCMD_JPEGD_IO_SIZE_2, + VCMD_JPEGD_INT_PIN_2, + VCMD_JPEGD_MODULE_TYPE_2, + VCMD_JPEGD_MODULE_MAIN_ADDR_2, + VCMD_JPEGD_MODULE_DEC400_ADDR_2, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_2, + VCMD_JPEGD_MODULE_MMU_ADDR_2}, + + {VCMD_JPEGD_IO_ADDR_3, + VCMD_JPEGD_IO_SIZE_3, + VCMD_JPEGD_INT_PIN_3, + VCMD_JPEGD_MODULE_TYPE_3, + VCMD_JPEGD_MODULE_MAIN_ADDR_3, + VCMD_JPEGD_MODULE_DEC400_ADDR_3, + VCMD_JPEGD_MODULE_L2CACHE_ADDR_3, + VCMD_JPEGD_MODULE_MMU_ADDR_3}, +#endif +}; +/*these size need to be modified according to hw config.*/ +#define VCMD_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_DECODER_REGISTER_SIZE (512 * 4) +#define VCMD_IM_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_ENCODER_REGISTER_SIZE (479 * 4) +#define VCMD_JPEG_DECODER_REGISTER_SIZE (512 * 4) + +#define MAX_VCMD_NUMBER (MAX_VCMD_TYPE*MAX_SAME_MODULE_TYPE_CORE_NUMBER) // + + +#define HW_WORK_STATE_PEND 3 + +#define MAX_CMDBUF_INT_NUMBER 1 +#define INT_MIN_SUM_OF_IMAGE_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_CMDBUF_INT_NUMBER) +#define MAX_PROCESS_CORE_NUMBER 4*8 +#define PROCESS_MAX_SUM_OF_IMAGE_SIZE (4096*2160*MAX_SAME_MODULE_TYPE_CORE_NUMBER*MAX_PROCESS_CORE_NUMBER) + + +#define MAX_SAME_MODULE_TYPE_CORE_NUMBER 4 + +#define VC8000E_MAX_CONFIG_LEN 32 + +#define VC8000E_PM_TIMEOUT 100 /* ms */ + +static size_t base_ddr_addr = 0; /*pcie address need to substract this value then can be put to register*/ + +#ifdef HANTROAXIFE_SUPPORT +#define AXIFE_SIZE (64*4) +volatile u8* axife_hwregs[MAX_VCMD_NUMBER][2]; +#endif + +#ifdef HANTROMMU_SUPPORT +#define MMU_SIZE (228*4) +extern unsigned int mmu_enable; +extern unsigned long gBaseDDRHw; +static volatile u8* mmu_hwregs[MAX_VCMD_NUMBER][2]; +#else +static unsigned int mmu_enable = 0; +#endif + +/********variables declaration related with race condition**********/ + +#define CMDBUF_MAX_SIZE (512*4*4) + + +#define CMDBUF_POOL_TOTAL_SIZE (2*1024*1024) //approximately=128x(320x240)=128x2k=128x8kbyte=1Mbytes +#define TOTAL_DISCRETE_CMDBUF_NUM (CMDBUF_POOL_TOTAL_SIZE/CMDBUF_MAX_SIZE) +#define CMDBUF_VCMD_REGISTER_TOTAL_SIZE 9*1024*1024-CMDBUF_POOL_TOTAL_SIZE*2 +#define VCMD_REGISTER_SIZE (128*4) + +#ifndef DYNAMIC_MALLOC_VCMDNODE +static struct cmdbuf_obj *g_cmdbuf_obj_pool; +static struct bi_list_node *g_cmdbuf_node_pool; +#endif + +struct noncache_mem +{ + u32 *virtualAddress; + dma_addr_t busAddress; + unsigned int mmu_bus_address; /* buffer physical address in MMU*/ + u32 size; + u16 cmdbuf_id; +}; + +struct process_manager_obj +{ + struct file *filp; + u32 total_exe_time; + u32 pm_count; + spinlock_t spinlock; + wait_queue_head_t wait_queue; +} ; + +struct cmdbuf_obj +{ + u32 module_type; //current CMDBUF type: input vc8000e=0,IM=1,vc8000d=2,jpege=3, jpegd=4 + u32 priority; //current CMDBUFpriority: normal=0, high=1 + u32 executing_time; //current CMDBUFexecuting_time=encoded_image_size*(rdoLevel+1)*(rdoq+1); + u32 cmdbuf_size; //current CMDBUF size + u32 *cmdbuf_virtualAddress; //current CMDBUF start virtual address. + size_t cmdbuf_busAddress; //current CMDBUF start physical address. + unsigned int mmu_cmdbuf_busAddress; //current CMDBUF start mmu mapping address. + u32 *status_virtualAddress; //current status CMDBUF start virtual address. + size_t status_busAddress; //current status CMDBUF start physical address. + unsigned int mmu_status_busAddress; //current status CMDBUF start mmu mapping address. + u32 status_size; //current status CMDBUF size + u32 executing_status; //current CMDBUF executing status. + struct file *filp; //file pointer in the same process. + u16 core_id; //which vcmd core is used. + u16 cmdbuf_id; //used to manage CMDBUF in driver.It is a handle to identify cmdbuf.also is an interrupt vector.position in pool,same as status position. + u8 cmdbuf_data_loaded; //0 means sw has not copied data into this CMDBUF; 1 means sw has copied data into this CMDBUF + u8 cmdbuf_data_linked; //0 :not linked, 1:linked.into a vcmd core list. + u8 cmdbuf_run_done; //if 0,waiting for CMDBUF finish; if 1, op code in CMDBUF has finished one by one. HANTRO_VCMD_IOCH_WAIT_CMDBUF will check this variable. + u8 cmdbuf_need_remove; // if 0, not need to remove CMDBUF; 1 CMDBUF can be removed if it is not the last CMDBUF; + u8 has_end_cmdbuf; //if 1, the last opcode is end opCode. + u8 no_normal_int_cmdbuf; //if 1, JMP will not send normal interrupt. + struct process_manager_obj* process_manager_obj; +}; + +struct hantrovcmd_dev +{ + struct vcmd_config vcmd_core_cfg; //config of each core,such as base addr, irq,etc + u32 core_id; //vcmd core id for driver and sw internal use + u32 sw_cmdbuf_rdy_num; + spinlock_t* spinlock; + wait_queue_head_t * wait_queue; + wait_queue_head_t * wait_abort_queue; + bi_list list_manager; + volatile u8 *hwregs;/* IO mem base */ + u32 reg_mirror[ASIC_VCMD_SWREG_AMOUNT]; + u32 duration_without_int; //number of cmdbufs without interrupt. + volatile u8 working_state; + u32 total_exe_time; + u16 status_cmdbuf_id;//used for analyse configuration in cwl. + u32 hw_version_id; /*megvii 0x43421001, later 0x43421102*/ + u32 *vcmd_reg_mem_virtualAddress;//start virtual address of vcmd registers memory of CMDBUF. + size_t vcmd_reg_mem_busAddress; //start physical address of vcmd registers memory of CMDBUF. + unsigned int mmu_vcmd_reg_mem_busAddress; //start mmu mapping address of vcmd registers memory of CMDBUF. + u32 vcmd_reg_mem_size; // size of vcmd registers memory of CMDBUF. + struct platform_device *pdev; + struct clk *cclk; + struct clk *aclk; + struct clk *pclk; + char config_buf[VC8000E_MAX_CONFIG_LEN]; + int has_power_domains; +} ; + +/* + * Ioctl definitions + */ +#define VCMD_HW_ID 0x4342 + + +static struct noncache_mem vcmd_buf_mem_pool; +static struct noncache_mem vcmd_status_buf_mem_pool; +static struct noncache_mem vcmd_registers_mem_pool; + +static u16 cmdbuf_used[TOTAL_DISCRETE_CMDBUF_NUM]; +static u16 cmdbuf_used_pos; +static u16 cmdbuf_used_residual; + +static struct hantrovcmd_dev* vcmd_manager[MAX_VCMD_TYPE][MAX_VCMD_NUMBER]; +static bi_list_node* global_cmdbuf_node[TOTAL_DISCRETE_CMDBUF_NUM]; + +static bi_list global_process_manager; + +static u16 vcmd_position[MAX_VCMD_TYPE]; +static int vcmd_type_core_num[MAX_VCMD_TYPE]; + +#define EXECUTING_CMDBUF_ID_ADDR 26 +#define VCMD_EXE_CMDBUF_COUNT 3 + +#define WORKING_STATE_IDLE 0 +#define WORKING_STATE_WORKING 1 +#define CMDBUF_EXE_STATUS_OK 0 +#define CMDBUF_EXE_STATUS_CMDERR 1 +#define CMDBUF_EXE_STATUS_BUSERR 2 + + +struct semaphore vcmd_reserve_cmdbuf_sem[MAX_VCMD_TYPE]; //for reserve + +//#define VCMD_DEBUG_INTERNAL + +/***************************TYPE AND FUNCTION DECLARATION****************/ + +/* here's all the must remember stuff */ + + +static int vcmd_reserve_IO(void); +static void vcmd_release_IO(void); +static void vcmd_reset_asic(struct hantrovcmd_dev * dev); +static void vcmd_reset_current_asic(struct hantrovcmd_dev * dev); +static int allocate_cmdbuf(struct noncache_mem* new_cmdbuf_addr,struct noncache_mem* new_status_cmdbuf_addr); +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node); +static void vcmd_start(struct hantrovcmd_dev *dev,bi_list_node* first_linked_cmdbuf_node); +static void create_kernel_process_manager(void); +static void vcmd_reset(void); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static irqreturn_t hantrovcmd_isr(int irq, void *dev_id, struct pt_regs *regs); +#else +static irqreturn_t hantrovcmd_isr(int irq, void *dev_id); +#endif + +#ifdef VCMD_DEBUG_INTERNAL +static void printk_vcmd_register_debug(const void *hwregs, char* info); +#endif + +/*********************local variable declaration*****************/ +static unsigned long vcmd_sram_base = 0; +static unsigned int vcmd_sram_size = 0; +/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/ +static int hantrovcmd_major = 0; +static int hantrovcmd_minor = 0; /* dynamic allocation */ +static struct cdev hantrovcmd_cdev; +static dev_t hantrovcmd_devt; +static struct class *hantrovcmd_class; +static int total_vcmd_core_num = 0; +/* dynamic allocation*/ +static struct hantrovcmd_dev* hantrovcmd_data = NULL; +static struct dentry *root_debugfs_dir = NULL; + +static int software_triger_abort=0; + +//#define IRQ_SIMULATION + +#ifdef IRQ_SIMULATION +struct timer_manager +{ + u32 core_id; //vcmd core id for driver and sw internal use + u32 timer_id; + struct timer_list *timer; +} ; + +static struct timer_list timer[10000]; +struct timer_manager timer_reserve[10000]; +#if 0 +static struct timer_list timer0; +static struct timer_list timer1; +#endif +#endif + + +//hw_queue can be used for reserve cmdbuf memory +DECLARE_WAIT_QUEUE_HEAD(vcmd_cmdbuf_memory_wait); + +DEFINE_SPINLOCK(vcmd_cmdbuf_alloc_lock); +DEFINE_SPINLOCK(vcmd_process_manager_lock); + + +static spinlock_t owner_lock_vcmd[MAX_VCMD_NUMBER]; + +static wait_queue_head_t wait_queue_vcmd[MAX_VCMD_NUMBER]; + +static wait_queue_head_t abort_queue_vcmd[MAX_VCMD_NUMBER]; + +#if 0 +/*allocate non-cacheable DMA memory*/ +#define DRIVER_NAME_HANTRO_NON_CACH_MEM "non_cach_memory" + +static struct platform_device *noncachable_mem_dev = NULL; + + +static const struct platform_device_info hantro_platform_info = { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + + +static int hantro_noncachable_mem_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + vcmd_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.virtualAddress = dma_alloc_coherent(dev,CMDBUF_POOL_TOTAL_SIZE,&vcmd_status_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + vcmd_status_buf_mem_pool.size = CMDBUF_POOL_TOTAL_SIZE; + return 0; +} + +static int hantro_noncachable_mem_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dma_free_coherent(dev,vcmd_buf_mem_pool.size,vcmd_buf_mem_pool.virtualAddress,vcmd_buf_mem_pool.busAddress); + dma_free_coherent(dev,vcmd_status_buf_mem_pool.size,vcmd_status_buf_mem_pool.virtualAddress,vcmd_status_buf_mem_pool.busAddress); + + return 0; +} + + +static const struct platform_device_id hantro_noncachable_mem_platform_ids[]={ + { + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + }, + {/* sentinel */}, +}; + + +static const struct of_device_id hantro_of_match[]={ + { + .compatible = "thead,light-vc8000e", + }, + {/* sentinel */}, +}; + +static struct platform_driver hantro_noncachable_mem_platform_driver = { + .probe = hantro_noncachable_mem_probe, + .remove = hantro_noncachable_mem_remove, + .driver ={ + .name = DRIVER_NAME_HANTRO_NON_CACH_MEM, + .owner = THIS_MODULE, + .of_match_table = hantro_of_match, + }, + .id_table = hantro_noncachable_mem_platform_ids, +}; + +static void init_vcmd_non_cachable_memory_allocate(void) +{ + + /*create device: This will create a {struct platform_device}, It has a member dev, which is a {struct device} */ + noncachable_mem_dev = platform_device_register_full(&hantro_platform_info); + + /*when this function is called, the .probe callback is invoked.*/ + platform_driver_register(&hantro_noncachable_mem_platform_driver); + + +} + +static void release_vcmd_non_cachable_memory(void) +{ + + /* when this fucntion is called, .remove callback will be invoked. use it to clean up all resources allocated in .probe.*/ + platform_driver_unregister(&hantro_noncachable_mem_platform_driver); + + /*destroy the device*/ + platform_device_unregister(noncachable_mem_dev); +} +#endif +/**********************************************************************************************************\ +*cmdbuf object management +\***********************************************************************************************************/ +static struct cmdbuf_obj* create_cmdbuf_obj(void) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + cmdbuf_obj=vmalloc(sizeof(struct cmdbuf_obj)); + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","vmalloc for cmdbuf_obj fail!"); + return cmdbuf_obj; + } + memset(cmdbuf_obj,0,sizeof(struct cmdbuf_obj)); + return cmdbuf_obj; +} + +static void free_cmdbuf_obj(struct cmdbuf_obj* cmdbuf_obj) +{ +#ifdef DYNAMIC_MALLOC_VCMDNODE + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(cmdbuf_obj); + return; +#endif +} + +static struct cmdbuf_obj *create_vcmd_cmdbuf_obj(u16 cmdbuf_id) +{ + struct cmdbuf_obj *cmdbuf_obj = NULL; + +#ifdef DYNAMIC_MALLOC_VCMDNODE + cmdbuf_obj = create_cmdbuf_obj(); +#else + cmdbuf_obj = g_cmdbuf_obj_pool + cmdbuf_id; + if (cmdbuf_obj) + memset(cmdbuf_obj, 0, sizeof(struct cmdbuf_obj)); +#endif + return cmdbuf_obj; +} + +static bi_list_node *bi_list_create_vcmd_node(u16 cmdbuf_id) +{ + bi_list_node *node = NULL; + +#ifdef DYNAMIC_MALLOC_VCMDNODE + node = bi_list_create_node(); +#else + node = g_cmdbuf_node_pool + cmdbuf_id; + if (node) + memset(node, 0, sizeof(bi_list_node)); +#endif + return node; +} + +static void free_cmdbuf_mem(u16 cmdbuf_id ) +{ + unsigned long flags; + + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + cmdbuf_used[cmdbuf_id]=0; + cmdbuf_used_residual +=1; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); +} + +static bi_list_node* create_cmdbuf_node(void) +{ + bi_list_node* current_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + struct noncache_mem new_cmdbuf_addr; + struct noncache_mem new_status_cmdbuf_addr; + + if(wait_event_interruptible(vcmd_cmdbuf_memory_wait, allocate_cmdbuf(&new_cmdbuf_addr,&new_status_cmdbuf_addr)) ) + return NULL; + cmdbuf_obj = create_vcmd_cmdbuf_obj(new_cmdbuf_addr.cmdbuf_id); + if(cmdbuf_obj==NULL) + { + PDEBUG ("%s\n","create_vcmd_cmdbuf_obj fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); + return NULL; + } + cmdbuf_obj->cmdbuf_busAddress = new_cmdbuf_addr.busAddress; + cmdbuf_obj->mmu_cmdbuf_busAddress = new_cmdbuf_addr.mmu_bus_address; + cmdbuf_obj->cmdbuf_virtualAddress = new_cmdbuf_addr.virtualAddress; + cmdbuf_obj->cmdbuf_size = new_cmdbuf_addr.size; + cmdbuf_obj->cmdbuf_id = new_cmdbuf_addr.cmdbuf_id; + cmdbuf_obj->status_busAddress = new_status_cmdbuf_addr.busAddress; + cmdbuf_obj->mmu_status_busAddress = new_status_cmdbuf_addr.mmu_bus_address; + cmdbuf_obj->status_virtualAddress = new_status_cmdbuf_addr.virtualAddress; + cmdbuf_obj->status_size = new_status_cmdbuf_addr.size; + current_node=bi_list_create_vcmd_node(cmdbuf_obj->cmdbuf_id); + if(current_node==NULL) + { + PDEBUG ("%s\n","bi_list_create_vcmd_node fail!"); + free_cmdbuf_mem(new_cmdbuf_addr.cmdbuf_id); + free_cmdbuf_obj(cmdbuf_obj); + return NULL; + } + current_node->data = (void*)cmdbuf_obj; + current_node->next = NULL; + current_node->previous = NULL; + return current_node; +} +static void free_cmdbuf_node(bi_list_node* cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + if(cmdbuf_node==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_node NULL"); + return; + } + cmdbuf_obj = (struct cmdbuf_obj*)cmdbuf_node->data; + //free cmdbuf mem in pool + free_cmdbuf_mem(cmdbuf_obj->cmdbuf_id); + + //free struct cmdbuf_obj + free_cmdbuf_obj(cmdbuf_obj); +#ifdef DYNAMIC_MALLOC_VCMDNODE + //free current cmdbuf_node entity. + bi_list_free_node(cmdbuf_node); +#endif + return; +} + +//just remove, not free the node. +static bi_list_node* remove_cmdbuf_node_from_list(bi_list* list,bi_list_node* cmdbuf_node) +{ + if(cmdbuf_node==NULL) + { + PDEBUG ("%s\n","remove_cmdbuf_node_from_list NULL"); + return NULL; + } + if(cmdbuf_node->next) + { + bi_list_remove_node(list,cmdbuf_node); + return cmdbuf_node; + } + else + { + //the last one, should not be removed. + return NULL; + } +} +//calculate executing_time of each vcmd +static u32 calculate_executing_time_after_node(bi_list_node* exe_cmdbuf_node) +{ + u32 time_run_all=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + while(1) + { + if(exe_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} +static u32 calculate_executing_time_after_node_high_priority(bi_list_node* exe_cmdbuf_node) +{ + u32 time_run_all=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + if(exe_cmdbuf_node==NULL) + return time_run_all; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + while(1) + { + if(exe_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj* )exe_cmdbuf_node->data; + if(cmdbuf_obj_temp->priority==CMDBUF_PRIORITY_NORMAL) + break; + time_run_all += cmdbuf_obj_temp->executing_time; + exe_cmdbuf_node = exe_cmdbuf_node->next; + } + return time_run_all; +} + + +/**********************************************************************************************************\ +*cmdbuf pool management +\***********************************************************************************************************/ +static int allocate_cmdbuf(struct noncache_mem* new_cmdbuf_addr,struct noncache_mem* new_status_cmdbuf_addr) +{ + unsigned long flags; + spin_lock_irqsave(&vcmd_cmdbuf_alloc_lock, flags); + if(cmdbuf_used_residual==0) + { + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + //no empty cmdbuf + return 0; + } + //there is one cmdbuf at least + while(1) + { + if(cmdbuf_used[cmdbuf_used_pos]==0&&(global_cmdbuf_node[cmdbuf_used_pos]==NULL )) + { + cmdbuf_used[cmdbuf_used_pos]=1; + cmdbuf_used_residual -=1; + new_cmdbuf_addr->virtualAddress=vcmd_buf_mem_pool.virtualAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE/4; + new_cmdbuf_addr->busAddress=vcmd_buf_mem_pool.busAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_cmdbuf_addr->mmu_bus_address=vcmd_buf_mem_pool.mmu_bus_address + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_cmdbuf_addr->size=CMDBUF_MAX_SIZE; + new_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + new_status_cmdbuf_addr->virtualAddress=vcmd_status_buf_mem_pool.virtualAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE/4; + new_status_cmdbuf_addr->busAddress=vcmd_status_buf_mem_pool.busAddress + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->mmu_bus_address=vcmd_status_buf_mem_pool.mmu_bus_address + cmdbuf_used_pos*CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->size=CMDBUF_MAX_SIZE; + new_status_cmdbuf_addr->cmdbuf_id = cmdbuf_used_pos; + cmdbuf_used_pos++; + if(cmdbuf_used_pos>=TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos=0; + spin_unlock_irqrestore(&vcmd_cmdbuf_alloc_lock, flags); + return 1; + } + else + { + cmdbuf_used_pos++; + if(cmdbuf_used_pos>=TOTAL_DISCRETE_CMDBUF_NUM) + cmdbuf_used_pos=0; + } + } + return 0; +} + +static bi_list_node* get_cmdbuf_node_in_list_by_addr(size_t cmdbuf_addr,bi_list* list) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + new_cmdbuf_node=list->head; + while(1) + { + if(new_cmdbuf_node==NULL) + return NULL; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=cmdbuf_addr)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >cmdbuf_addr)) ) + { + return new_cmdbuf_node; + } + new_cmdbuf_node=new_cmdbuf_node->next; + } + return NULL; +} + +static int wait_abort_rdy(struct hantrovcmd_dev*dev) +{ + return dev->working_state == WORKING_STATE_IDLE; +} +static int select_vcmd(bi_list_node* new_cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* curr_cmdbuf_node=NULL; + bi_list* list=NULL; + struct hantrovcmd_dev*dev=NULL; + struct hantrovcmd_dev*smallest_dev=NULL; + u32 executing_time=0xffff; + int counter=0; + unsigned long flags=0; + u32 hw_rdy_cmdbuf_num=0; + size_t exe_cmdbuf_addr=0; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + u32 cmdbuf_id=0; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + //there is an empty vcmd to be used + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + if( list->tail==NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //there is a vcmd which tail node -> cmdbuf_run_done == 1. It means this vcmd has nothing to do, so we select it. + counter =0; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + curr_cmdbuf_node = list->tail; + if(curr_cmdbuf_node == NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_run_done ==1) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + + //another case, tail = executing node, and vcmd=pend state (finish but not generate interrupt) + counter =0; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + list=&dev->list_manager; + //read executing cmdbuf address + if(dev->hw_version_id <= HW_ID_1_0_C ) + hw_rdy_cmdbuf_num = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT); + else + { + hw_rdy_cmdbuf_num = *(dev->vcmd_reg_mem_virtualAddress+VCMD_EXE_CMDBUF_COUNT); + if(hw_rdy_cmdbuf_num!=dev->sw_cmdbuf_rdy_num) + hw_rdy_cmdbuf_num += 1; + } + spin_lock_irqsave(dev->spinlock, flags); + curr_cmdbuf_node = list->tail; + if(curr_cmdbuf_node == NULL) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + + if((dev->sw_cmdbuf_rdy_num ==hw_rdy_cmdbuf_num)) + { + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + cmdbuf_obj->core_id = dev->core_id; + return 0; + } + else + { + spin_unlock_irqrestore(dev->spinlock, flags); + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + } + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + + + + //there is no idle vcmd,if low priority,calculate exe time, select the least one. + // or if high priority, calculate the exe time, select the least one and abort it. + if(cmdbuf_obj->priority==CMDBUF_PRIORITY_NORMAL) + { + + counter =0; + //calculate total execute time of all devices + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + //read executing cmdbuf address + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + exe_cmdbuf_addr = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node=get_cmdbuf_node_in_list_by_addr(exe_cmdbuf_addr,list); + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + else + { + //cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR+1); + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM||cmdbuf_id == 0) + { + pr_err("cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return -1; + } + //get the executing cmdbuf node. + curr_cmdbuf_node=global_cmdbuf_node[cmdbuf_id]; + if(curr_cmdbuf_node==NULL) + { + list=&dev->list_manager; + curr_cmdbuf_node = list->head; + while(1) + { + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_data_linked&&cmdbuf_obj_temp->cmdbuf_run_done==0) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + + } + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //find the device with the least total_exe_time. + counter =0; + executing_time=0xffffffff; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->total_exe_time <= executing_time) + { + executing_time = dev->total_exe_time; + smallest_dev = dev; + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //insert list + list = &smallest_dev->list_manager; + spin_lock_irqsave(smallest_dev->spinlock, flags); + bi_list_insert_node_tail(list,new_cmdbuf_node); + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + cmdbuf_obj->core_id = smallest_dev->core_id; + return 0; + } + else + { + //CMDBUF_PRIORITY_HIGH + counter =0; + //calculate total execute time of all devices + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + //read executing cmdbuf address + exe_cmdbuf_addr = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + list=&dev->list_manager; + spin_lock_irqsave(dev->spinlock, flags); + //get the executing cmdbuf node. + curr_cmdbuf_node=get_cmdbuf_node_in_list_by_addr(exe_cmdbuf_addr,list); + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node_high_priority(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + else + { + //cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR); + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM||cmdbuf_id == 0) + { + pr_err("cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return -1; + } + //get the executing cmdbuf node. + curr_cmdbuf_node=global_cmdbuf_node[cmdbuf_id]; + if(curr_cmdbuf_node==NULL) + { + list=&dev->list_manager; + curr_cmdbuf_node = list->head; + while(1) + { + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp =(struct cmdbuf_obj*) curr_cmdbuf_node->data; + if(cmdbuf_obj_temp->cmdbuf_data_linked&&cmdbuf_obj_temp->cmdbuf_run_done==0) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + } + + //calculate total execute time of this device + dev->total_exe_time=calculate_executing_time_after_node(curr_cmdbuf_node); + spin_unlock_irqrestore(dev->spinlock, flags); + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //find the smallest device. + counter =0; + executing_time=0xffffffff; + while(1) + { + dev = vcmd_manager[cmdbuf_obj->module_type][vcmd_position[cmdbuf_obj->module_type]]; + if(dev->total_exe_time <= executing_time) + { + executing_time = dev->total_exe_time; + smallest_dev = dev; + } + vcmd_position[cmdbuf_obj->module_type]++; + if(vcmd_position[cmdbuf_obj->module_type]>=vcmd_type_core_num[cmdbuf_obj->module_type]) + vcmd_position[cmdbuf_obj->module_type]=0; + counter++; + if(counter>=vcmd_type_core_num[cmdbuf_obj->module_type]) + break; + } + //abort the vcmd and wait + vcmd_write_register_value((const void *)smallest_dev->hwregs,smallest_dev->reg_mirror,HWIF_VCMD_START_TRIGGER,0); + software_triger_abort = 1; + if(wait_event_interruptible(*smallest_dev->wait_abort_queue, wait_abort_rdy(smallest_dev)) ) + { + software_triger_abort = 0; + return -ERESTARTSYS; + } + software_triger_abort = 0; + //need to select inserting position again because hw maybe have run to the next node. + //CMDBUF_PRIORITY_HIGH + spin_lock_irqsave(smallest_dev->spinlock, flags); + curr_cmdbuf_node = smallest_dev->list_manager.head; + while(1) + { + //if list is empty or tail,insert to tail + if(curr_cmdbuf_node == NULL) + break; + cmdbuf_obj_temp= (struct cmdbuf_obj*)curr_cmdbuf_node->data; + //if find the first node which priority is normal, insert node prior to the node + if((cmdbuf_obj_temp->priority==CMDBUF_PRIORITY_NORMAL) && (cmdbuf_obj_temp->cmdbuf_run_done==0)) + break; + curr_cmdbuf_node = curr_cmdbuf_node->next; + } + bi_list_insert_node_before(list,curr_cmdbuf_node,new_cmdbuf_node); + cmdbuf_obj->core_id = smallest_dev->core_id; + spin_unlock_irqrestore(smallest_dev->spinlock, flags); + + return 0; + } + return 0; +} +static int wait_process_resource_rdy(struct process_manager_obj* process_manager_obj ) +{ + return process_manager_obj->total_exe_time<=PROCESS_MAX_SUM_OF_IMAGE_SIZE; +} + +static long reserve_cmdbuf(struct file *filp,struct exchange_parameter* input_para) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* process_manager_node=NULL; + struct process_manager_obj* process_manager_obj=NULL; + unsigned long flags; + input_para->cmdbuf_id = 0; + if(input_para->cmdbuf_size>CMDBUF_MAX_SIZE) + { + return -1; + } + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while(1) + { + if(process_manager_node == NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR process_manager_node !!\n"); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + return -1; + } + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + if (filp==process_manager_obj->filp) + { + break; + } + process_manager_node = process_manager_node->next; + } + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + spin_lock_irqsave(&process_manager_obj->spinlock, flags); + process_manager_obj->total_exe_time += input_para->executing_time; + spin_unlock_irqrestore(&process_manager_obj->spinlock, flags); + if(wait_event_interruptible(process_manager_obj->wait_queue, wait_process_resource_rdy(process_manager_obj))) + return -1; + + new_cmdbuf_node=create_cmdbuf_node(); + if(new_cmdbuf_node==NULL) + return -1; + + cmdbuf_obj = (struct cmdbuf_obj* )new_cmdbuf_node->data; + cmdbuf_obj->module_type = input_para->module_type; + cmdbuf_obj->priority = input_para->priority; + cmdbuf_obj->executing_time = input_para->executing_time; + cmdbuf_obj->cmdbuf_size = CMDBUF_MAX_SIZE; + input_para->cmdbuf_size =CMDBUF_MAX_SIZE; + cmdbuf_obj->filp = filp; + cmdbuf_obj->process_manager_obj =process_manager_obj; + + input_para->cmdbuf_id=cmdbuf_obj->cmdbuf_id; + global_cmdbuf_node[input_para->cmdbuf_id] = new_cmdbuf_node; + + return 0; +} + +static long release_cmdbuf(struct file *filp,u16 cmdbuf_id) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* last_cmdbuf_node=NULL; + bi_list_node* new_cmdbuf_node=NULL; + bi_list* list=NULL; + u32 module_type; + + + unsigned long flags; + struct hantrovcmd_dev* dev=NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + module_type = cmdbuf_obj->module_type; + //TODO + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[module_type])) + return -ERESTARTSYS; + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + + //spin_lock_irqsave(dev->spinlock, flags); + list=&dev->list_manager; + cmdbuf_obj->cmdbuf_need_remove=1; + last_cmdbuf_node = new_cmdbuf_node->previous; + while(1) + { + //remove current node + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_need_remove==1) + { + new_cmdbuf_node=remove_cmdbuf_node_from_list(list,new_cmdbuf_node); + if(new_cmdbuf_node) + { + //free node + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + if(cmdbuf_obj->process_manager_obj) + { + spin_lock_irqsave(&cmdbuf_obj->process_manager_obj->spinlock, flags); + cmdbuf_obj->process_manager_obj->total_exe_time -= cmdbuf_obj->executing_time; + spin_unlock_irqrestore(&cmdbuf_obj->process_manager_obj->spinlock, flags); + wake_up_interruptible_all(&cmdbuf_obj->process_manager_obj->wait_queue); + } + free_cmdbuf_node(new_cmdbuf_node); + + } + } + if(last_cmdbuf_node==NULL) + break; + new_cmdbuf_node=last_cmdbuf_node; + last_cmdbuf_node=new_cmdbuf_node->previous; + } + //spin_unlock_irqrestore(dev->spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[module_type]); + return 0; +} +static long release_cmdbuf_node(bi_list* list,bi_list_node*cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + /*get cmdbuf object according to cmdbuf_id*/ + new_cmdbuf_node=cmdbuf_node; + if(new_cmdbuf_node==NULL) + return -1; + //remove node from list + new_cmdbuf_node=remove_cmdbuf_node_from_list(list,new_cmdbuf_node); + if(new_cmdbuf_node) + { + //free node + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + return 0; + } + return 1; +} + +static long release_cmdbuf_node_cleanup(bi_list* list) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + while(1) + { + new_cmdbuf_node=list->head; + if(new_cmdbuf_node==NULL) + return 0; + //remove node from list + bi_list_remove_node(list,new_cmdbuf_node); + //free node + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(new_cmdbuf_node); + } + return 0; +} + + + +static bi_list_node* find_last_linked_cmdbuf(bi_list_node* current_node) +{ + bi_list_node* new_cmdbuf_node=current_node; + bi_list_node* last_cmdbuf_node; + struct cmdbuf_obj* cmdbuf_obj=NULL; + if(current_node==NULL) + return NULL; + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + while(1) + { + if(new_cmdbuf_node==NULL) + return last_cmdbuf_node; + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_data_linked) + { + return new_cmdbuf_node; + } + last_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + return NULL; +} +static long link_and_run_cmdbuf(struct file *filp,struct exchange_parameter* input_para) +{ + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* new_cmdbuf_node=NULL; + bi_list_node* last_cmdbuf_node; + u32* jmp_addr=NULL; + u32 opCode; + u32 tempOpcode; + u32 record_last_cmdbuf_rdy_num; + struct hantrovcmd_dev* dev=NULL; + unsigned long flags; + int return_value; + u16 cmdbuf_id=input_para->cmdbuf_id; + + + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj->cmdbuf_data_loaded=1; + cmdbuf_obj->cmdbuf_size=input_para->cmdbuf_size; +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + pr_info("vcmd link, current cmdbuf content\n"); + for(i=0;icmdbuf_size/4;i++) + { + pr_info("current cmdbuf data %d =0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } +#endif + //test nop and end opcode, then assign value. + cmdbuf_obj->has_end_cmdbuf=0; //0: has jmp opcode,1 has end code + cmdbuf_obj->no_normal_int_cmdbuf=0; //0: interrupt when JMP,1 not interrupt when JMP + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + opCode=tempOpcode = *(jmp_addr-4); + opCode >>=27; + opCode <<=27; + //we can't identify END opcode or JMP opcode, so we don't support END opcode in control sw and driver. + if(opCode == OPCODE_JMP) + { + //jmp + opCode=tempOpcode; + opCode &=0x02000000; + if(opCode == JMP_IE_1) + { + cmdbuf_obj->no_normal_int_cmdbuf=0; + } + else + { + cmdbuf_obj->no_normal_int_cmdbuf=1; + } + } + else + { + //not support other opcode + return -1; + } + + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type])) + return -ERESTARTSYS; + + return_value=select_vcmd(new_cmdbuf_node); + if(return_value) + return return_value; + + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; + input_para->core_id = cmdbuf_obj->core_id; + PDEBUG("Allocate cmd buffer [%d] to core [%d]\n", cmdbuf_id, input_para->core_id); + //set ddr address for vcmd registers copy. + if(dev->hw_version_id > HW_ID_1_0_C ) + { + //read vcmd executing register into ddr memory. + //now core id is got and output ddr address of vcmd register can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress; + if (mmu_enable) { + *(jmp_addr + 2) = 0; + *(jmp_addr+1) = (u32)((dev->mmu_vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr + 2) = (u32)((u64)(dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)>>32); + } else { + *(jmp_addr + 2) = 0; + } + *(jmp_addr+1) = (u32)((dev->vcmd_reg_mem_busAddress + (EXECUTING_CMDBUF_ID_ADDR+1)*4)); + } + + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + //read vcmd all registers into ddr memory. + //now core id is got and output ddr address of vcmd registers can be filled in. + //each core has its own fixed output ddr address of vcmd registers. + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-6) = 0; + } + *(jmp_addr-7) = (u32)(dev->mmu_vcmd_reg_mem_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-6) = (u32)((u64)dev->vcmd_reg_mem_busAddress>>32); + } else { + *(jmp_addr-6) = 0; + } + *(jmp_addr-7) = (u32)(dev->vcmd_reg_mem_busAddress); + } + } + //start to link and/or run + spin_lock_irqsave(dev->spinlock, flags); + last_cmdbuf_node = find_last_linked_cmdbuf(new_cmdbuf_node); + record_last_cmdbuf_rdy_num=dev->sw_cmdbuf_rdy_num; + vcmd_link_cmdbuf(dev,last_cmdbuf_node); + if(dev->working_state==WORKING_STATE_IDLE) + { + //run + while (last_cmdbuf_node && + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_run_done) + last_cmdbuf_node = last_cmdbuf_node->next; + + if (last_cmdbuf_node && last_cmdbuf_node->data) { + PDEBUG("vcmd start for cmdbuf id %d, cmdbuf_run_done = %d\n", + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_id, + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_run_done); + } + vcmd_start(dev,last_cmdbuf_node); + } + else + { + //just update cmdbuf ready number + if(record_last_cmdbuf_rdy_num!=dev->sw_cmdbuf_rdy_num) + vcmd_write_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev->sw_cmdbuf_rdy_num); + } + spin_unlock_irqrestore(dev->spinlock, flags); + + up(&vcmd_reserve_cmdbuf_sem[cmdbuf_obj->module_type]); + + + return 0; +} + +/******************************************************************************/ +static int check_cmdbuf_irq(struct hantrovcmd_dev* dev,struct cmdbuf_obj* cmdbuf_obj,u32 *irq_status_ret) +{ + + int rdy = 0; + unsigned long flags; + spin_lock_irqsave(dev->spinlock, flags); + if(cmdbuf_obj->cmdbuf_run_done) + { + rdy = 1; + *irq_status_ret=cmdbuf_obj->executing_status;//need to decide how to assign this variable + } + spin_unlock_irqrestore(dev->spinlock, flags); + return rdy; +} +#ifdef IRQ_SIMULATION +static void get_random_bytes(void *buf, int nbytes); +#if 0 +void hantrovcmd_trigger_irq_0(struct timer_list* timer) +{ + PDEBUG("trigger core 0 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[0]); +} +void hantrovcmd_trigger_irq_1(struct timer_list* timer) +{ + PDEBUG("trigger core 1 irq\n"); + del_timer(timer); + hantrovcmd_isr(0,(void *)&hantrovcmd_data[1]); +} +#else +static void hantrovcmd_trigger_irq(struct timer_list *timer) +{ + + u32 timer_id=0; + u32 core_id=0; + u32 i; + for(i=0;i<10000;i++) + { + if(timer_reserve[i].timer==timer) + { + timer_id=timer_reserve[i].timer_id; + core_id = timer_reserve[i].core_id; + break; + } + } + PDEBUG("trigger core 0 irq\n"); + hantrovcmd_isr(core_id,(void *)&hantrovcmd_data[core_id]); + del_timer(timer); + timer_reserve[timer_id].timer=NULL; +} + +#endif +#endif + +static unsigned int wait_cmdbuf_ready(struct file *filp,u16 cmdbuf_id,u32 *irq_status_ret) +{ + + struct cmdbuf_obj* cmdbuf_obj=NULL; + bi_list_node* new_cmdbuf_node=NULL; + struct hantrovcmd_dev* dev=NULL; + + PDEBUG("wait_cmdbuf_ready\n"); + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + cmdbuf_obj=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->filp!=filp) + { + //should not happen + pr_err("hantrovcmd: ERROR cmdbuf_id !!\n"); + return -1; + } + dev = &hantrovcmd_data[cmdbuf_obj->core_id]; +#ifdef IRQ_SIMULATION + { + u32 random_num; + //get_random_bytes(&random_num, sizeof(u32)); + random_num = (u32)((u64)100*cmdbuf_obj->executing_time/(4096*2160)+50); + PDEBUG("random_num=%d\n",random_num); +#if 0 + /*init a timer to trigger irq*/ + if (cmdbuf_obj->core_id==0) + { + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + timer_setup(&timer0,hantrovcmd_trigger_irq_0,0); + timer0.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer0); + } + + if (cmdbuf_obj->core_id==1) + { + //init_timer(&timer1); + //timer1.function = hantrovcmd_trigger_irq_1; + timer_setup(&timer1,hantrovcmd_trigger_irq_1,0); + timer1.expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(&timer1); + } +#else + { + u32 i; + struct timer_list *temp_timer=NULL; + for(i=0;i<10000;i++) + { + if(timer_reserve[i].timer==NULL) + { + timer_reserve[i].timer_id=i; + timer_reserve[i].core_id=cmdbuf_obj->core_id; + temp_timer=timer_reserve[i].timer =&timer[i] ; + break; + } + } + //if (cmdbuf_obj->core_id==0) + { + //init_timer(&timer0); + //timer0.function = hantrovcmd_trigger_irq_0; + timer_setup(temp_timer,hantrovcmd_trigger_irq,0); + temp_timer->expires = jiffies + random_num*HZ/10; //the expires time is 1s + add_timer(temp_timer); + } + } +#endif + } +#endif + + if(wait_event_interruptible(*dev->wait_queue, check_cmdbuf_irq(dev,cmdbuf_obj,irq_status_ret))) + { + PDEBUG("vcmd_wait_queue_0 interrupted\n"); + //abort the vcmd + vcmd_write_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_START_TRIGGER,0); + return -ERESTARTSYS; + } + return 0; +} + + +static long hantrovcmd_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct device *dev = &hantrovcmd_data->pdev->dev; + struct process_manager_obj* process_manager_obj=NULL; + + PDEBUG("ioctl cmd 0x%08x\n", cmd); + /* + * extract the type and number bitfields, and don't encode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if(_IOC_TYPE(cmd) != HANTRO_IOC_MAGIC +#ifdef HANTROMMU_SUPPORT + && _IOC_TYPE(cmd) != HANTRO_IOC_MMU +#endif + ) + return -ENOTTY; + if((_IOC_TYPE(cmd) == HANTRO_IOC_MAGIC && + _IOC_NR(cmd) > HANTRO_IOC_MAXNR) +#ifdef HANTROMMU_SUPPORT + ||(_IOC_TYPE(cmd) == HANTRO_IOC_MMU && + _IOC_NR(cmd) > HANTRO_IOC_MMU_MAXNR) +#endif + ) + return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if(_IOC_DIR(cmd) & _IOC_READ) +#if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); +#endif + else if(_IOC_DIR(cmd) & _IOC_WRITE) +#if KERNEL_VERSION(5,0,0) <= LINUX_VERSION_CODE + err = !access_ok((void *) arg, _IOC_SIZE(cmd)); +#else + err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); +#endif + if(err) + return -EFAULT; + + process_manager_obj = (struct process_manager_obj*)filp->private_data; + + switch (cmd) + { + case HANTRO_IOCH_GET_VCMD_ENABLE: + { + __put_user(1, (unsigned long *) arg); + break; + } + case HANTRO_IOCH_GET_CMDBUF_PARAMETER: + { + struct cmdbuf_mem_parameter local_cmdbuf_mem_data; + PDEBUG(" VCMD GET_CMDBUF_PARAMETER\n"); + local_cmdbuf_mem_data.cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_unit_size = CMDBUF_MAX_SIZE; + local_cmdbuf_mem_data.cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.status_cmdbuf_total_size = CMDBUF_POOL_TOTAL_SIZE; + local_cmdbuf_mem_data.phy_status_cmdbuf_addr = vcmd_status_buf_mem_pool.busAddress; + local_cmdbuf_mem_data.phy_cmdbuf_addr = vcmd_buf_mem_pool.busAddress; + if (mmu_enable) { + local_cmdbuf_mem_data.mmu_phy_status_cmdbuf_addr = vcmd_status_buf_mem_pool.mmu_bus_address; + local_cmdbuf_mem_data.mmu_phy_cmdbuf_addr = vcmd_buf_mem_pool.mmu_bus_address; + } else { + local_cmdbuf_mem_data.mmu_phy_status_cmdbuf_addr = 0; + local_cmdbuf_mem_data.mmu_phy_cmdbuf_addr = 0; + } + local_cmdbuf_mem_data.base_ddr_addr = base_ddr_addr; + copy_to_user((struct cmdbuf_mem_parameter*)arg,&local_cmdbuf_mem_data,sizeof(struct cmdbuf_mem_parameter)); + break; + } + case HANTRO_IOCH_GET_VCMD_PARAMETER: + { + struct config_parameter input_para; + PDEBUG(" VCMD get vcmd config parameter \n"); + copy_from_user(&input_para,(struct config_parameter*)arg,sizeof(struct config_parameter)); + if(vcmd_type_core_num[input_para.module_type]) + { + input_para.submodule_main_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr; + input_para.submodule_dec400_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_dec400_addr; + input_para.submodule_L2Cache_addr = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr; + input_para.submodule_MMU_addr[0] = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_MMU_addr[0]; + input_para.submodule_MMU_addr[1] = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_MMU_addr[1]; + input_para.submodule_axife_addr[0] = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_axife_addr[0]; + input_para.submodule_axife_addr[1] = vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_axife_addr[1]; + input_para.config_status_cmdbuf_id = vcmd_manager[input_para.module_type][0]->status_cmdbuf_id; + input_para.vcmd_hw_version_id = vcmd_manager[input_para.module_type][0]->hw_version_id; + input_para.vcmd_core_num = vcmd_type_core_num[input_para.module_type]; + } + else + { + input_para.submodule_main_addr = 0xffff; + input_para.submodule_dec400_addr = 0xffff; + input_para.submodule_L2Cache_addr = 0xffff; + input_para.submodule_MMU_addr[0] = 0xffff; + input_para.submodule_MMU_addr[1] = 0xffff; + input_para.submodule_axife_addr[0] = 0xffff; + input_para.submodule_axife_addr[1] = 0xffff; + input_para.config_status_cmdbuf_id = 0; + input_para.vcmd_core_num = 0; + input_para.vcmd_hw_version_id =HW_ID_1_0_C; + } + copy_to_user((struct config_parameter*)arg,&input_para,sizeof(struct config_parameter)); + break; + } + case HANTRO_IOCH_RESERVE_CMDBUF: + { + int ret; + struct exchange_parameter input_para; + copy_from_user(&input_para,(struct exchange_parameter*)arg,sizeof(struct exchange_parameter)); + ret = reserve_cmdbuf(filp,&input_para); + if (ret == 0) + copy_to_user((struct exchange_parameter*)arg,&input_para,sizeof(struct exchange_parameter)); + PDEBUG(" VCMD Reserve CMDBUF %d\n", input_para.cmdbuf_id); + return ret; + } + + case HANTRO_IOCH_LINK_RUN_CMDBUF: + { + struct exchange_parameter input_para; + long retVal; + copy_from_user(&input_para,(struct exchange_parameter*)arg,sizeof(struct exchange_parameter)); + + PDEBUG("VCMD link and run cmdbuf\n"); + pm_runtime_resume_and_get(dev); + if (process_manager_obj) + process_manager_obj->pm_count++; + retVal = link_and_run_cmdbuf(filp,&input_para); + copy_to_user((struct exchange_parameter*)arg,&input_para,sizeof(struct exchange_parameter)); + return retVal; + break; + } + + case HANTRO_IOCH_WAIT_CMDBUF: + { + + u16 cmdbuf_id; + unsigned int tmp; + u32 irq_status_ret=0; + __get_user(cmdbuf_id, (u16*)arg); + /*high 16 bits are core id, low 16 bits are cmdbuf_id*/ + + PDEBUG("VCMD wait for CMDBUF finishing. \n"); + + //TODO + tmp = wait_cmdbuf_ready(filp,cmdbuf_id,&irq_status_ret); + cmdbuf_id=(u16)irq_status_ret; + if (tmp==0) + { + __put_user(cmdbuf_id, (u16 *)arg); + return tmp;//return core_id + } + else + { + return -1; + } + + break; + } + case HANTRO_IOCH_RELEASE_CMDBUF: + { + u16 cmdbuf_id; + __get_user(cmdbuf_id, (u16*)arg); + /*16 bits are cmdbuf_id*/ + + PDEBUG("VCMD release CMDBUF\n"); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (process_manager_obj) + process_manager_obj->pm_count--; + release_cmdbuf(filp,cmdbuf_id); + return 0; + break; + } + case HANTRO_IOCH_POLLING_CMDBUF: + { + u16 core_id; + __get_user(core_id, (u16*)arg); + /*16 bits are cmdbuf_id*/ + if(core_id>=total_vcmd_core_num) + return -1; + hantrovcmd_isr(core_id,&hantrovcmd_data[core_id]); + return 0; + break; + } + default: + { +#ifdef HANTROMMU_SUPPORT + if(_IOC_TYPE(cmd) == HANTRO_IOC_MMU) + { + pm_runtime_resume_and_get(dev); + long retval = MMUIoctl(cmd, filp, arg, mmu_hwregs); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return retval; + } +#endif + } + } + return 0; +} +/**********************************************************************************************************\ +*process manager object management +\***********************************************************************************************************/ +static struct process_manager_obj* create_process_manager_obj(void) +{ + struct process_manager_obj* process_manager_obj=NULL; + process_manager_obj=vmalloc(sizeof(struct process_manager_obj)); + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","vmalloc for process_manager_obj fail!"); + return process_manager_obj; + } + memset(process_manager_obj,0,sizeof(struct process_manager_obj)); + return process_manager_obj; +} + +static void free_process_manager_obj( struct process_manager_obj* process_manager_obj) +{ + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","free_process_manager_obj NULL"); + return; + } + //free current cmdbuf_obj + vfree(process_manager_obj); + return; +} + +static bi_list_node* create_process_manager_node(void) +{ + bi_list_node* current_node=NULL; + struct process_manager_obj* process_manager_obj=NULL; + + process_manager_obj=create_process_manager_obj(); + if(process_manager_obj==NULL) + { + PDEBUG ("%s\n","create_process_manager_obj fail!"); + return NULL; + } + process_manager_obj->total_exe_time = 0; + process_manager_obj->pm_count = 0; + spin_lock_init(&process_manager_obj->spinlock); + init_waitqueue_head(&process_manager_obj->wait_queue); + current_node=bi_list_create_node(); + if(current_node==NULL) + { + PDEBUG ("%s\n","bi_list_create_node fail!"); + free_process_manager_obj(process_manager_obj); + return NULL; + } + current_node->data = (void*)process_manager_obj; + return current_node; +} +static void free_process_manager_node(bi_list_node* process_node) +{ + struct process_manager_obj* process_manager_obj=NULL; + if(process_node==NULL) + { + PDEBUG ("%s\n","free_process_manager_node NULL"); + return; + } + process_manager_obj = (struct process_manager_obj*)process_node->data; + //free struct process_manager_obj + free_process_manager_obj (process_manager_obj); + //free current process_manager_obj entity. + bi_list_free_node(process_node); + return; +} + +static long release_process_node_cleanup(bi_list* list) +{ + bi_list_node* new_process_node=NULL; + + while(1) + { + new_process_node=list->head; + if(new_process_node==NULL) + break; + //remove node from list + bi_list_remove_node(list,new_process_node); + //remove node from list + free_process_manager_node(new_process_node); + } + return 0; +} + +static void create_kernel_process_manager(void) +{ + bi_list_node* process_manager_node; + struct process_manager_obj* process_manager_obj=NULL; + process_manager_node = create_process_manager_node(); + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + process_manager_obj->filp = NULL; + bi_list_insert_node_tail(&global_process_manager,process_manager_node); +} + +/* Update the last JMP cmd in cmdbuf_ojb in order to jump to next_cmdbuf_obj. */ +static void cmdbuf_update_jmp_cmd(int hw_version_id, + struct cmdbuf_obj *cmdbuf_obj, + struct cmdbuf_obj *next_cmdbuf_obj, + int jmp_IE_1) { + u32 *jmp_addr; + u32 operation_code; + + if(!cmdbuf_obj) + return; + + if(cmdbuf_obj->has_end_cmdbuf==0) + { + //need to link, current cmdbuf link to next cmdbuf + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + if (!next_cmdbuf_obj) { + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code & ~JMP_RDY_1); + } else { + if(hw_version_id > HW_ID_1_0_C ) + { + //set next cmdbuf id + *(jmp_addr-1) = next_cmdbuf_obj->cmdbuf_id; + } + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->mmu_cmdbuf_busAddress)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->mmu_cmdbuf_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr); + } + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code |JMP_RDY_1|jmp_IE_1|((next_cmdbuf_obj->cmdbuf_size+7)/8)); + } + +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + pr_info("vcmd link, last cmdbuf content\n"); + for(i=cmdbuf_obj->cmdbuf_size/4 -8;icmdbuf_size/4;i++) + { + pr_info("current linked cmdbuf data %d =0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } +#endif + } +} + + +/* delink given cmd buffer (cmdbuf_node) and remove it from list. + Also modify the last JMP of buf P to point to cmdbuf N. + Used when a process is terminated but there are pending cmd bufs in vmcd list. + E.g., + before: + + L->L->...->P->X->N-> ... ->L + ^ ^ ^ + head cmdbuf_node tail + + end: + + L->L->...->P->N-> ... ->L + ^ ^ + head tail + + Return: pointer to N or NULL if N doesn't exist. + */ +void vcmd_delink_rm_cmdbuf(struct hantrovcmd_dev *dev, bi_list_node* cmdbuf_node) +{ + bi_list *list = &dev->list_manager; + struct cmdbuf_obj* cmdbuf_obj = (struct cmdbuf_obj*)cmdbuf_node->data; + bi_list_node* prev = cmdbuf_node->previous; + bi_list_node* next = cmdbuf_node->next; + + PDEBUG("Delink and remove cmdbuf [%d] from vcmd list.\n", cmdbuf_obj->cmdbuf_id); + if (prev) { + PDEBUG("prev cmdbuf [%d].\n", ((struct cmdbuf_obj*)prev->data)->cmdbuf_id); + } else { + PDEBUG("NO prev cmdbuf.\n"); + } + if (next) { + PDEBUG("next cmdbuf [%d].\n", ((struct cmdbuf_obj*)next->data)->cmdbuf_id); + } else { + PDEBUG("NO next cmdbuf.\n"); + } + + bi_list_remove_node(list, cmdbuf_node); + global_cmdbuf_node[cmdbuf_obj->cmdbuf_id] = NULL; + free_cmdbuf_node(cmdbuf_node); + + cmdbuf_update_jmp_cmd(dev->hw_version_id, prev ? prev->data : NULL, next ? next->data : NULL, + dev->duration_without_int > INT_MIN_SUM_OF_IMAGE_SIZE); +} + +static int hantrovcmd_open(struct inode *inode, struct file *filp) +{ + int result = 0; + struct hantrovcmd_dev *dev = hantrovcmd_data; + bi_list_node* process_manager_node; + unsigned long flags; + struct process_manager_obj* process_manager_obj=NULL; + + filp->private_data = NULL; + process_manager_node = create_process_manager_node(); + if(process_manager_node== NULL) + return -1; + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + process_manager_obj->filp = filp; + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + bi_list_insert_node_tail(&global_process_manager,process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + filp->private_data = process_manager_node->data; + + PDEBUG("dev opened\n"); + return result; +} +static int hantrovcmd_release(struct inode *inode, struct file *filp) +{ + struct hantrovcmd_dev *dev = hantrovcmd_data; + u32 core_id = 0; + u32 release_cmdbuf_num=0; + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj_temp=NULL; + bi_list_node* process_manager_node; + struct process_manager_obj* process_manager_obj=NULL; + int vcmd_aborted = 0; // vcmd is aborted in this function + struct cmdbuf_obj* restart_cmdbuf = NULL; + + unsigned long flags; + long retVal=0; + + PDEBUG("dev closed for process %p\n", (void *)filp); + if (down_interruptible(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type])) + return -ERESTARTSYS; + + for (core_id = 0;core_id < total_vcmd_core_num; core_id++) + { + if((&dev[core_id])==NULL) + continue; + spin_lock_irqsave(dev[core_id].spinlock, flags); + new_cmdbuf_node=dev[core_id].list_manager.head; + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj_temp=(struct cmdbuf_obj*)new_cmdbuf_node->data; + if (dev[core_id].hwregs && (cmdbuf_obj_temp->filp == filp)) + { + if(cmdbuf_obj_temp->cmdbuf_run_done) + { + cmdbuf_obj_temp->cmdbuf_need_remove=1; + retVal=release_cmdbuf_node(&dev[core_id].list_manager,new_cmdbuf_node); + if(retVal==1) + cmdbuf_obj_temp->process_manager_obj = NULL; + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==0) + { + cmdbuf_obj_temp->cmdbuf_data_linked = 1; + cmdbuf_obj_temp->cmdbuf_run_done=1; + cmdbuf_obj_temp->cmdbuf_need_remove=1; + retVal=release_cmdbuf_node(&dev[core_id].list_manager,new_cmdbuf_node); + if(retVal==1) + cmdbuf_obj_temp->process_manager_obj = NULL; + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==1 && dev[core_id].working_state==WORKING_STATE_IDLE) + { + vcmd_delink_rm_cmdbuf(&dev[core_id], new_cmdbuf_node); + if(restart_cmdbuf == cmdbuf_obj_temp) + restart_cmdbuf = new_cmdbuf_node->next ? new_cmdbuf_node->next->data : NULL; + } + else if(cmdbuf_obj_temp->cmdbuf_data_linked==1 && dev[core_id].working_state==WORKING_STATE_WORKING) + { + bi_list_node* last_cmdbuf_node = NULL; + bi_list_node* done_cmdbuf_node = NULL; + int abort_cmdbuf_id; + int loop_count = 0; + + //abort the vcmd and wait + PDEBUG("Abort due to linked cmdbuf %d of current process.\n", cmdbuf_obj_temp->cmdbuf_id); +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug((const void *)dev[core_id].hwregs, "Before trigger to 0"); +#endif + // disable abort interrupt + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER,0); + vcmd_aborted = 1; + software_triger_abort = 1; +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug((const void *)dev[core_id].hwregs,"After trigger to 0"); +#endif + // wait vcmd core aborted and vcmd enters IDLE mode. + while (vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_WORK_STATE)) { + loop_count++; + if (!(loop_count % 10)) { + u32 irq_status = vcmd_read_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + pr_err("hantrovcmd: expected idle state, but irq status = 0x%0x\n", irq_status); + pr_err("hantrovcmd: vcmd current status is %d\n", vcmd_get_register_value((const void *)dev[core_id].hwregs, dev[core_id].reg_mirror, HWIF_VCMD_WORK_STATE)); + } + mdelay(10); // wait 10ms + if (loop_count > 100) { // too long + pr_err("hantrovcmd: too long before vcmd core to IDLE state\n"); + process_manager_obj = (struct process_manager_obj*)filp->private_data; + if (process_manager_obj) + { + while(process_manager_obj->pm_count > 0) + { + pm_runtime_mark_last_busy(&dev[0].pdev->dev); + pm_runtime_put_autosuspend(&dev[0].pdev->dev); + process_manager_obj->pm_count--; + } + } + spin_unlock_irqrestore(dev[core_id].spinlock, flags); + up(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type]); + return -ERESTARTSYS; + } + } + dev[core_id].working_state = WORKING_STATE_IDLE; + // clear interrupt & restore abort_e + if (vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_IRQ_ABORT)) { + PDEBUG("Abort interrupt triggered, now clear it to avoid abort int...\n"); + vcmd_write_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, 0x1<<4); + PDEBUG("Now irq status = 0x%0x.\n", vcmd_read_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET)); + } + + abort_cmdbuf_id = vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + PDEBUG("Abort when executing cmd buf %d.\n", abort_cmdbuf_id); + dev[core_id].sw_cmdbuf_rdy_num = 0; + dev[core_id].duration_without_int = 0; + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT,0); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,0); + + /* Mark cmdbuf_run_done to 1 for all the cmd buf executed. */ + done_cmdbuf_node = dev[core_id].list_manager.head; + while (done_cmdbuf_node) { + if (!((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_run_done) { + ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_run_done = 1; + ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_data_linked = 0; + PDEBUG("Set cmdbuf [%d] cmdbuf_run_done to 1.\n", ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id); + } + if (((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id == abort_cmdbuf_id) + break; + done_cmdbuf_node = done_cmdbuf_node->next; + } + if (cmdbuf_obj_temp->cmdbuf_run_done) { + /* current cmdbuf is in fact has been executed, but due to interrupt is not triggered, the status is not updated. + Just delink and remove it from the list. */ + if (done_cmdbuf_node && done_cmdbuf_node->data) { + PDEBUG("done_cmdbuf_node is cmdbuf [%d].\n", ((struct cmdbuf_obj*)done_cmdbuf_node->data)->cmdbuf_id); + } + done_cmdbuf_node = done_cmdbuf_node->next; + if (done_cmdbuf_node) + restart_cmdbuf = (struct cmdbuf_obj*)done_cmdbuf_node->data; + if (restart_cmdbuf) { + PDEBUG("Set restart cmdbuf [%d] via if.\n", restart_cmdbuf->cmdbuf_id); + } + } else { + last_cmdbuf_node = new_cmdbuf_node; + /* cmd buf num from aborted cmd buf to current cmdbuf_obj_temp */ + if (cmdbuf_obj_temp->cmdbuf_id != abort_cmdbuf_id) { + last_cmdbuf_node = new_cmdbuf_node->previous; + + while (last_cmdbuf_node && + ((struct cmdbuf_obj*)last_cmdbuf_node->data)->cmdbuf_id != abort_cmdbuf_id) { + restart_cmdbuf = (struct cmdbuf_obj*)last_cmdbuf_node->data; + last_cmdbuf_node = last_cmdbuf_node->previous; + dev[core_id].sw_cmdbuf_rdy_num++; + dev[core_id].duration_without_int += restart_cmdbuf->executing_time; + PDEBUG("Keep valid cmdbuf [%d] in the list.\n", restart_cmdbuf->cmdbuf_id); + } + } + if (restart_cmdbuf) { + PDEBUG("Set restart cmdbuf [%d] via else.\n", restart_cmdbuf->cmdbuf_id); + } + } + + // remove first linked cmdbuf from list + vcmd_delink_rm_cmdbuf(&dev[core_id], new_cmdbuf_node); + + } + software_triger_abort = 0; + release_cmdbuf_num++; + PDEBUG("release reserved cmdbuf\n"); + } + else if (vcmd_aborted && !cmdbuf_obj_temp->cmdbuf_run_done) { + /* VCMD is aborted, need to re-calculate the duration_without_int */ + if (!restart_cmdbuf) + restart_cmdbuf = cmdbuf_obj_temp; /* first cmdbuf to be restarted */ + dev[core_id].duration_without_int += cmdbuf_obj_temp->executing_time; + dev[core_id].sw_cmdbuf_rdy_num++; + } + new_cmdbuf_node = new_cmdbuf_node->next; + } + if (restart_cmdbuf && restart_cmdbuf->core_id == core_id) { + u32 irq_status1, irq_status2; + PDEBUG("Restart from cmdbuf [%d] after aborting.\n", restart_cmdbuf->cmdbuf_id); + + irq_status1 = vcmd_read_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + vcmd_write_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET, irq_status1); + irq_status2 = vcmd_read_reg((const void *)dev[core_id].hwregs, VCMD_REGISTER_INT_STATUS_OFFSET); + PDEBUG("Clear irq status from 0x%0x -> 0x%0x\n", irq_status1, irq_status2); + if (mmu_enable) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR, + (u32)(restart_cmdbuf->mmu_cmdbuf_busAddress)); + if(sizeof(size_t) == 8) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(restart_cmdbuf->mmu_cmdbuf_busAddress)>>32)); + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(restart_cmdbuf->cmdbuf_busAddress-base_ddr_addr)); + if(sizeof(size_t) == 8) { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(restart_cmdbuf->cmdbuf_busAddress-base_ddr_addr)>>32)); + } else { + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror, HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_COUNT,0); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_EXE_CMDBUF_LENGTH,(u32)((restart_cmdbuf->cmdbuf_size+7)/8)); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID,restart_cmdbuf->cmdbuf_id); + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev->sw_cmdbuf_rdy_num); +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug((const void *)dev[core_id].hwregs, "before restart"); +#endif + vcmd_write_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER,1); + + PDEBUG("Restart from cmdbuf [%d] dev register sw_cmdbuf_rdy_num is %d \n", restart_cmdbuf->cmdbuf_id, + vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT)); + + PDEBUG("Restart from cmdbuf [%d] after aborting: start trigger = %d.\n", restart_cmdbuf->cmdbuf_id, + vcmd_get_register_value((const void *)dev[core_id].hwregs,dev[core_id].reg_mirror,HWIF_VCMD_START_TRIGGER)); + PDEBUG("dev state from %d -> WORKING.\n", dev[core_id].working_state); + dev[core_id].working_state = WORKING_STATE_WORKING; +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug((const void *)dev[core_id].hwregs, "after restart"); +#endif + } else { + PDEBUG("No more command buffer to be restarted!\n"); + } + spin_unlock_irqrestore(dev[core_id].spinlock, flags); + // VCMD aborted but not restarted, nedd to wake up + if (vcmd_aborted && !restart_cmdbuf) + wake_up_interruptible_all(dev[core_id].wait_queue); + } + if(release_cmdbuf_num) + wake_up_interruptible_all(&vcmd_cmdbuf_memory_wait); + spin_lock_irqsave(&vcmd_process_manager_lock, flags); + process_manager_node = global_process_manager.head; + while(1) + { + if(process_manager_node == NULL) + break; + process_manager_obj = (struct process_manager_obj*)process_manager_node->data; + if(process_manager_obj->filp == filp) + break; + process_manager_node = process_manager_node->next; + } + if (process_manager_obj) + { + while(process_manager_obj->pm_count > 0) + { + pm_runtime_mark_last_busy(&dev[0].pdev->dev); + pm_runtime_put_autosuspend(&dev[0].pdev->dev); + process_manager_obj->pm_count--; + } + } + //remove node from list + bi_list_remove_node(&global_process_manager,process_manager_node); + spin_unlock_irqrestore(&vcmd_process_manager_lock, flags); + free_process_manager_node(process_manager_node); + up(&vcmd_reserve_cmdbuf_sem[dev->vcmd_core_cfg.sub_module_type]); + return 0; +} + +static bool hantroenc_cmdbuf_range(size_t addr, size_t size); + +static int mmap_cmdbuf_mem(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + /* Does it even fit in phys_addr_t? */ + if (offset >> PAGE_SHIFT != vma->vm_pgoff) + return -EINVAL; + + /* It's illegal to wrap around the end of the physical address space. */ + if (offset + (phys_addr_t)size - 1 < offset) + return -EINVAL; + + + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, + size, + vma->vm_page_prot); + + /* Remap-pfn-range will mark the range VM_IO */ + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + size, + vma->vm_page_prot)) + { + return -EAGAIN; + } + + return 0; +} + +static int mmap_mem(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + if (hantroenc_cmdbuf_range(offset, size)) + { + return mmap_cmdbuf_mem(file, vma); + } + else + { + return -EINVAL; + /*TODO: need check if not need enc,in this condition*/ + //return allocator_mmap(file,vma); + } +} + +/* VFS methods */ +static struct file_operations hantrovcmd_fops = { + .owner= THIS_MODULE, + .open = hantrovcmd_open, + .release = hantrovcmd_release, + .unlocked_ioctl = hantrovcmd_ioctl, + .mmap = mmap_mem, + .fasync = NULL, +}; + +static u32 vcmd_release_AXIFE_IO(void) +{ +#ifdef HANTROAXIFE_SUPPORT + int i=0, j=0; + + for(i=0; idev, CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE, + vcmd_buf_mem_pool.virtualAddress, vcmd_buf_mem_pool.busAddress); + + return 0; +} + +static u32 MMU_Kernel_unmap(void) +{ +#ifdef HANTROMMU_SUPPORT + struct kernel_addr_desc addr; + + if (vcmd_buf_mem_pool.virtualAddress) { + if (mmu_enable) { + addr.bus_address = vcmd_buf_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_buf_mem_pool.size; + MMUKernelMemNodeUnmap(&addr); + } + } + if (vcmd_status_buf_mem_pool.virtualAddress) { + if (mmu_enable) { + addr.bus_address = vcmd_status_buf_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_status_buf_mem_pool.size; + MMUKernelMemNodeUnmap(&addr); + } + } + if (vcmd_registers_mem_pool.virtualAddress) { + if (mmu_enable) { + addr.bus_address = vcmd_registers_mem_pool.busAddress - gBaseDDRHw; + addr.size = vcmd_registers_mem_pool.size; + MMUKernelMemNodeUnmap(&addr); + } + } +#endif + + return 0; +} + +static void vcmd_link_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + bi_list_node* next_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + struct cmdbuf_obj* next_cmdbuf_obj=NULL; + u32 * jmp_addr=NULL; + u32 operation_code; + new_cmdbuf_node = last_linked_cmdbuf_node; + //for the first cmdbuf. + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_data_linked==0) ) + { + dev->sw_cmdbuf_rdy_num ++; + cmdbuf_obj->cmdbuf_data_linked=1; + dev->duration_without_int = 0; + if(cmdbuf_obj->has_end_cmdbuf==0) + { + if(cmdbuf_obj->no_normal_int_cmdbuf==1) + { + dev->duration_without_int = cmdbuf_obj->executing_time; + //maybe nop is modified, so write back. + if(dev->duration_without_int>=INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + operation_code = *(jmp_addr-4); + operation_code = JMP_IE_1|operation_code; + *(jmp_addr-4) = operation_code; + dev->duration_without_int = 0; + } + + } + } + } + } + while(1) + { + if(new_cmdbuf_node==NULL) + break; + if(new_cmdbuf_node->next==NULL) + break; + next_cmdbuf_node = new_cmdbuf_node->next; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + next_cmdbuf_obj = (struct cmdbuf_obj*)next_cmdbuf_node->data; + if(cmdbuf_obj->has_end_cmdbuf==0 && !next_cmdbuf_obj->cmdbuf_run_done) + { + //need to link, current cmdbuf link to next cmdbuf + jmp_addr = cmdbuf_obj->cmdbuf_virtualAddress + (cmdbuf_obj->cmdbuf_size/4); + if(dev->hw_version_id > HW_ID_1_0_C ) + { + //set next cmdbuf id + *(jmp_addr-1) = next_cmdbuf_obj->cmdbuf_id; + } + if (mmu_enable) { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->mmu_cmdbuf_busAddress)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->mmu_cmdbuf_busAddress); + pr_debug("vcmd_link_cmdbuf: next_cmdbuf_obj->mmu_cmdbuf_busAddress=0x%08x\n", next_cmdbuf_obj->mmu_cmdbuf_busAddress); + } else { + if(sizeof(size_t) == 8) { + *(jmp_addr-2)=(u32)((u64)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32); + } else { + *(jmp_addr-2)=0; + } + *(jmp_addr-3)=(u32)(next_cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr); + } + operation_code = *(jmp_addr-4); + operation_code >>=16; + operation_code <<=16; + *(jmp_addr-4)=(u32)(operation_code |JMP_RDY_1|((next_cmdbuf_obj->cmdbuf_size+7)/8)); + next_cmdbuf_obj->cmdbuf_data_linked = 1; + dev->sw_cmdbuf_rdy_num ++; + //modify nop code of next cmdbuf + if(next_cmdbuf_obj->has_end_cmdbuf==0) + { + if(next_cmdbuf_obj->no_normal_int_cmdbuf==1) + { + dev->duration_without_int +=next_cmdbuf_obj->executing_time; + + //maybe we see the modified nop before abort, so need to write back. + if(dev->duration_without_int>=INT_MIN_SUM_OF_IMAGE_SIZE) + { + jmp_addr = next_cmdbuf_obj->cmdbuf_virtualAddress + (next_cmdbuf_obj->cmdbuf_size/4); + operation_code = *(jmp_addr-4); + operation_code = JMP_IE_1|operation_code; + *(jmp_addr-4) = operation_code; + dev->duration_without_int = 0; + } + } + } + else + { + dev->duration_without_int = 0; + } +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i; + pr_info("vcmd link, last cmdbuf content\n"); + for(i=cmdbuf_obj->cmdbuf_size/4 -8;icmdbuf_size/4;i++) + { + pr_info("current linked cmdbuf data %d =0x%x\n",i,*(cmdbuf_obj->cmdbuf_virtualAddress+i)); + } + } +#endif + } + new_cmdbuf_node = new_cmdbuf_node->next; + } + return; +} +/* delink all the cmd buffers from the cmdbuf in front of last_linked_cmdbuf_node + to head of the list. All the cmd bufs marked as X will be delinked. + E.g., + X->X->...->X->L->L-> ... ->L + ^ ^ ^ + head last_linked_cmdbuf_node tail + */ + +static void vcmd_delink_cmdbuf(struct hantrovcmd_dev *dev,bi_list_node* last_linked_cmdbuf_node) +{ + bi_list_node* new_cmdbuf_node=NULL; + struct cmdbuf_obj* cmdbuf_obj=NULL; + + new_cmdbuf_node = last_linked_cmdbuf_node; + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if(cmdbuf_obj->cmdbuf_data_linked) + { + cmdbuf_obj->cmdbuf_data_linked = 0; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + dev->sw_cmdbuf_rdy_num=0; +} + +static void ConfigAIIXFE_MMU_BYVCMD(struct hantrovcmd_dev **device) +{ +#ifdef HANTROVCMD_ENABLE_IP_SUPPORT + u32 i = 0; + u64 address = 0; + u32 mirror_index, register_index, register_value; + u32 write_command = 0; + if(!device) return; + struct hantrovcmd_dev *dev = *device; + + mirror_index = VCMD_REGISTER_INDEX_SW_INIT_CMD0; + write_command = OPCODE_WREG|(1<<26)|(1<<16); +#ifdef HANTROAXIFE_SUPPORT + //enable AXIFE by VCMD + for(i=0; i<2; i++) + { + if(dev->vcmd_core_cfg.submodule_axife_addr[i] != 0xffff) + { + register_index = AXI_REG10_SW_FRONTEND_EN; + register_value = 0x02; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_axife_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + + register_index = AXI_REG11_SW_WORK_MODE; + register_value = 0x00; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_axife_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + } + } +#endif +#ifdef HANTROMMU_SUPPORT + //enable MMU by VCMD + address = GetMMUAddress(); + pr_info("ConfigAIIXFE_MMU_BYVCMD: address = 0x%llx", address); + for(i=0; i<2; i++) + { + if(dev->vcmd_core_cfg.submodule_MMU_addr[i] != 0xffff) + { + register_index = MMU_REG_ADDRESS; + register_value = address; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_MMU_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + + register_index = MMU_REG_PAGE_TABLE_ID; + register_value = 0x10000; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_MMU_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + + register_index = MMU_REG_PAGE_TABLE_ID; + register_value = 0x00000; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_MMU_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + + register_index = MMU_REG_CONTROL; + register_value = 1; + dev->reg_mirror[mirror_index++] = write_command|(dev->vcmd_core_cfg.submodule_MMU_addr[i]+register_index); + dev->reg_mirror[mirror_index++] = register_value; + } + } +#endif + //END command + dev->reg_mirror[mirror_index++] = OPCODE_END; + dev->reg_mirror[mirror_index] = 0x00; + + for(i=0; ihwregs, register_index, dev->reg_mirror[i+VCMD_REGISTER_INDEX_SW_INIT_CMD0]); + } +#endif +} +static void vcmd_start(struct hantrovcmd_dev *dev,bi_list_node* first_linked_cmdbuf_node) +{ + struct cmdbuf_obj* cmdbuf_obj = NULL; + + if(dev->working_state == WORKING_STATE_IDLE) + { + if((first_linked_cmdbuf_node!=NULL) && dev->sw_cmdbuf_rdy_num) + { + cmdbuf_obj = (struct cmdbuf_obj*)first_linked_cmdbuf_node->data; +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug((const void *)dev->hwregs, "vcmd_start enters"); +#endif + //0x40 +#ifdef HANTROVCMD_ENABLE_IP_SUPPORT + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_INIT_MODE,1); //when start vcmd, first vcmd is init mode +#endif + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_AXI_CLK_GATE_DISABLE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE,1);//this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_CORE_CLK_GATE_DISABLE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_ABORT_MODE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RESET_CORE,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RESET_ALL,0); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_START_TRIGGER,0); + //0x48 + if(dev->hw_version_id <= HW_ID_1_0_C) + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_INTCMD_EN,0xffff); + else + { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_JMPP_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_JMPD_EN,1); + } + + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_RESET_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ABORT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_CMDERR_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_TIMEOUT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_BUSERR_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ENDCMD_EN,1); + //0x4c + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_TIMEOUT_EN,1); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_TIMEOUT_CYCLES,0x1dcd6500); + if (mmu_enable) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(cmdbuf_obj->mmu_cmdbuf_busAddress)); + pr_debug("cmdbuf_obj->mmu_cmdbuf_busAddress=0x%08x\n", (u32)cmdbuf_obj->mmu_cmdbuf_busAddress); + if(sizeof(size_t) == 8) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(cmdbuf_obj->mmu_cmdbuf_busAddress)>>32)); + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR,(u32)(cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)); + if(sizeof(size_t) == 8) { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB,(u32)((u64)(cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr)>>32)); + } else { + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, 0); + } + } + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_EXE_CMDBUF_LENGTH,(u32)((cmdbuf_obj->cmdbuf_size+7)/8)); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_RDY_CMDBUF_COUNT,dev->sw_cmdbuf_rdy_num); + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MAX_BURST_LEN,0x10); + if(dev->hw_version_id > HW_ID_1_0_C ) + { + vcmd_write_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID,(u32)cmdbuf_obj->cmdbuf_id); + } + vcmd_write_reg((const void *)dev->hwregs,0x44,vcmd_read_reg((const void *)dev->hwregs,0x44)); + vcmd_write_reg((const void *)dev->hwregs,0x40,dev->reg_mirror[0x40/4]); + vcmd_write_reg((const void *)dev->hwregs,0x48,dev->reg_mirror[0x48/4]); + vcmd_write_reg((const void *)dev->hwregs,0x4c,dev->reg_mirror[0x4c/4]); + vcmd_write_reg((const void *)dev->hwregs,0x50,dev->reg_mirror[0x50/4]); + vcmd_write_reg((const void *)dev->hwregs,0x54,dev->reg_mirror[0x54/4]); + vcmd_write_reg((const void *)dev->hwregs,0x58,dev->reg_mirror[0x58/4]); + vcmd_write_reg((const void *)dev->hwregs,0x5c,dev->reg_mirror[0x5c/4]); + vcmd_write_reg((const void *)dev->hwregs,0x60,dev->reg_mirror[0x60/4]); + vcmd_write_reg((const void *)dev->hwregs,0x64,0xffffffff);//not interrupt cpu + + dev->working_state = WORKING_STATE_WORKING; + + ConfigAIIXFE_MMU_BYVCMD(&dev); + + //start + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE,1);//this bit should be set 1 only when need to reset dec400 + vcmd_set_register_mirror_value(dev->reg_mirror,HWIF_VCMD_START_TRIGGER,1); + PDEBUG("To write vcmd register 16:0x%x\n",dev->reg_mirror[0x40/4]); + vcmd_write_reg((const void *)dev->hwregs,0x40,dev->reg_mirror[0x40/4]); +#ifdef VCMD_DEBUG_INTERNAL + printk_vcmd_register_debug(dev->hwregs, "vcmd_start exits "); +#endif + } + } + +} + +static void create_read_all_registers_cmdbuf(struct exchange_parameter* input_para) +{ + u32 register_range[]={VCMD_ENCODER_REGISTER_SIZE, + VCMD_IM_REGISTER_SIZE, + VCMD_DECODER_REGISTER_SIZE, + VCMD_JPEG_ENCODER_REGISTER_SIZE, + VCMD_JPEG_DECODER_REGISTER_SIZE}; + u32 counter_cmdbuf_size=0; + u32 * set_base_addr=vcmd_buf_mem_pool.virtualAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE/4; + //u32 *status_base_virt_addr=vcmd_status_buf_mem_pool.virtualAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE/4+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2/4+0); + ptr_t status_base_phy_addr=vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2+0); + u32 map_status_base_phy_addr=vcmd_status_buf_mem_pool.mmu_bus_address + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr/2+0); + u32 offset_inc=0; + u32 offset_inc_dec400=0; + if(vcmd_manager[input_para->module_type][0]->hw_version_id>HW_ID_1_0_C) + { + pr_info("vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n",vcmd_manager[input_para->module_type][0]->hw_version_id); + + //read vcmd executing cmdbuf id registers to ddr for balancing core load. + *(set_base_addr+0) = (OPCODE_RREG) |(1<<16) |(EXECUTING_CMDBUF_ID_ADDR*4); + counter_cmdbuf_size += 4; + *(set_base_addr+1) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr+2) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+3) = 0; + counter_cmdbuf_size += 4; + + //read main IP all registers + *(set_base_addr+4) = (OPCODE_RREG) |((register_range[input_para->module_type]/4)<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+5) = map_status_base_phy_addr; + } else { + *(set_base_addr+5) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+6) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+6) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+6) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+7) = 0; + counter_cmdbuf_size += 4; + + if(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr != 0xffff) + { + //read L2 cache register + offset_inc = 4; + status_base_phy_addr=vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr/2+0); + map_status_base_phy_addr=vcmd_status_buf_mem_pool.mmu_bus_address + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr/2+0); + //read L2cache IP first register + *(set_base_addr+8) = (OPCODE_RREG) |(1<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_L2Cache_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+9) = map_status_base_phy_addr; + } else { + *(set_base_addr+9) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+10) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+10) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+10) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+11) = 0; + counter_cmdbuf_size += 4; + } + if(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr != 0xffff) + { + //read dec400 register + offset_inc_dec400 = 4; + status_base_phy_addr=vcmd_status_buf_mem_pool.busAddress + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr/2+0); + map_status_base_phy_addr=vcmd_status_buf_mem_pool.mmu_bus_address + input_para->cmdbuf_id*CMDBUF_MAX_SIZE+(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr/2+0); + //read DEC400 IP first register + *(set_base_addr+8+offset_inc) = (OPCODE_RREG) |(0x2b<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_dec400_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) + { + *(set_base_addr+9+offset_inc) = map_status_base_phy_addr; + } + else + { + *(set_base_addr+9+offset_inc) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) + { + *(set_base_addr+10+offset_inc) = 0; + } + else + { + if(sizeof(size_t) == 8) + { + *(set_base_addr+10+offset_inc) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } + else + { + *(set_base_addr+10+offset_inc) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+11+offset_inc) = 0; + counter_cmdbuf_size += 4; + } + +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //read vcmd registers to ddr + *(set_base_addr+8+offset_inc + offset_inc_dec400) = (OPCODE_RREG) |(27<<16) |(0); + counter_cmdbuf_size += 4; + *(set_base_addr+9+offset_inc + offset_inc_dec400) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + *(set_base_addr+10+offset_inc + offset_inc_dec400) = (u32)0; //will be changed in link stage + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+11+offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + //JMP RDY = 0 + *(set_base_addr +12+offset_inc + offset_inc_dec400)= (OPCODE_JMP_RDY0) |0 |JMP_IE_1|0; + counter_cmdbuf_size += 4; + *(set_base_addr +13+offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +14+offset_inc + offset_inc_dec400) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +15+offset_inc + offset_inc_dec400) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size=(16+offset_inc + offset_inc_dec400)*4; + } + else + { + pr_info("vc8000_vcmd_driver:create cmdbuf data when hw_version_id = 0x%x\n",vcmd_manager[input_para->module_type][0]->hw_version_id); + //read all registers + *(set_base_addr+0) = (OPCODE_RREG) |((register_range[input_para->module_type]/4)<<16) |(vcmd_manager[input_para->module_type][0]->vcmd_core_cfg.submodule_main_addr+0); + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+1) = map_status_base_phy_addr; + } else { + *(set_base_addr+1) = (u32)(status_base_phy_addr-base_ddr_addr); + } + counter_cmdbuf_size += 4; + if (mmu_enable) { + *(set_base_addr+2) = 0; + } else { + if(sizeof(size_t) == 8) { + *(set_base_addr+2) = (u32)((u64)(status_base_phy_addr-base_ddr_addr)>>32); + } else { + *(set_base_addr+2) = 0; + } + } + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+3) = 0; + counter_cmdbuf_size += 4; +#if 0 + //INT code, interrupt immediately + *(set_base_addr+4) = (OPCODE_INT) |0 |input_para->cmdbuf_id; + counter_cmdbuf_size += 4; + //alignment + *(set_base_addr+5) = 0; + counter_cmdbuf_size += 4; +#endif + //JMP RDY = 0 + *(set_base_addr +4)= (OPCODE_JMP_RDY0) |0 |JMP_IE_1|0; + counter_cmdbuf_size += 4; + *(set_base_addr +5) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +6) = 0; + counter_cmdbuf_size += 4; + *(set_base_addr +7) = input_para->cmdbuf_id; + //don't add the last alignment DWORD in order to identify END command or JMP command. + //counter_cmdbuf_size += 4; + input_para->cmdbuf_size=8*4; + } + +} +static void read_main_module_all_registers(u32 main_module_type) +{ + int ret; + struct exchange_parameter input_para; + u32 irq_status_ret=0; + u32 *status_base_virt_addr; + + input_para.executing_time=0; + input_para.priority=CMDBUF_PRIORITY_NORMAL; + input_para.module_type = main_module_type; + input_para.cmdbuf_size=0; + ret = reserve_cmdbuf(NULL,&input_para); + vcmd_manager[main_module_type][0]->status_cmdbuf_id = input_para.cmdbuf_id; + create_read_all_registers_cmdbuf(&input_para); + link_and_run_cmdbuf(NULL,&input_para); + //msleep(1000); + hantrovcmd_isr(input_para.core_id, &hantrovcmd_data[input_para.core_id]); + wait_cmdbuf_ready(NULL,input_para.cmdbuf_id,&irq_status_ret); + status_base_virt_addr=vcmd_status_buf_mem_pool.virtualAddress + input_para.cmdbuf_id*CMDBUF_MAX_SIZE/4+(vcmd_manager[input_para.module_type][0]->vcmd_core_cfg.submodule_main_addr/2/4+0); + pr_info("vc8000_vcmd_driver: main module register 0:0x%x\n",*status_base_virt_addr); + pr_info("vc8000_vcmd_driver: main module register 80:0x%x\n",*(status_base_virt_addr+80)); + pr_info("vc8000_vcmd_driver: main module register 214:0x%x\n",*(status_base_virt_addr+214)); + pr_info("vc8000_vcmd_driver: main module register 226:0x%x\n", *(status_base_virt_addr+226)); + pr_info("vc8000_vcmd_driver: main module register 287:0x%x\n", *(status_base_virt_addr+287)); + //don't release cmdbuf because it can be used repeatedly + //release_cmdbuf(input_para.cmdbuf_id); +} + +/*------------------------------------------------------------------------------ + Function name : vcmd_pcie_init + Description : Initialize PCI Hw access + + Return type : int + ------------------------------------------------------------------------------*/ +static int vcmd_init(struct platform_device *pdev) +{ + struct pci_dev *g_vcmd_dev = NULL; /* PCI device structure. */ + unsigned long g_vcmd_base_ddr_hw; /* PCI base register address (memalloc) */ + + vcmd_buf_mem_pool.virtualAddress = dma_alloc_coherent(&pdev->dev, CMDBUF_POOL_TOTAL_SIZE*2 + CMDBUF_VCMD_REGISTER_TOTAL_SIZE, + &vcmd_buf_mem_pool.busAddress, GFP_KERNEL | GFP_DMA); + + pr_info("Base memory val 0x%llx\n", vcmd_buf_mem_pool.busAddress); + + vcmd_buf_mem_pool.size =CMDBUF_POOL_TOTAL_SIZE; + pr_info("Init: vcmd_buf_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_buf_mem_pool.busAddress); + + if (vcmd_buf_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_buf_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_buf_mem_pool.virtualAddress); + + vcmd_status_buf_mem_pool.busAddress = (void *)vcmd_buf_mem_pool.busAddress+CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.virtualAddress = (void *)vcmd_buf_mem_pool.virtualAddress+CMDBUF_POOL_TOTAL_SIZE; + vcmd_status_buf_mem_pool.size =CMDBUF_POOL_TOTAL_SIZE; + pr_info("Init: vcmd_status_buf_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_status_buf_mem_pool.busAddress); + + if (vcmd_status_buf_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_status_buf_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_status_buf_mem_pool.virtualAddress); + + vcmd_registers_mem_pool.busAddress = (void *)vcmd_buf_mem_pool.busAddress+CMDBUF_POOL_TOTAL_SIZE*2; + vcmd_registers_mem_pool.virtualAddress = (void *)vcmd_buf_mem_pool.virtualAddress+CMDBUF_POOL_TOTAL_SIZE*2; + vcmd_registers_mem_pool.size =CMDBUF_VCMD_REGISTER_TOTAL_SIZE; + pr_info("Init: vcmd_registers_mem_pool.busAddress=0x%llx.\n",(long long unsigned int)vcmd_registers_mem_pool.busAddress); + + if (vcmd_registers_mem_pool.virtualAddress == NULL ) { + pr_info("Init: failed to ioremap.\n"); + return -1; + } + pr_info("Init: vcmd_registers_mem_pool.virtualAddress=0x%llx.\n",(long long unsigned int)vcmd_registers_mem_pool.virtualAddress); + + return 0; + +out_pci_disable_device: + pci_disable_device(g_vcmd_dev); +out: + return -1; +} + +static ssize_t encoder_config_write(struct file *filp, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct hantrovcmd_dev *dev = hantrovcmd_data; + unsigned long value; + int ret; + + if (count > VC8000E_MAX_CONFIG_LEN) + count = VC8000E_MAX_CONFIG_LEN; + else if (count <= 2) + return 0; + + ret = copy_from_user(dev->config_buf, userbuf, count); + if (ret) { + ret = -EFAULT; + goto out; + } + + //pr_info("hantrodec config: %s\n", dev->config_buf); + switch (dev->config_buf[0]) { + case 'd': + value = simple_strtoul(&(dev->config_buf[1]), NULL, 10); + pm_runtime_set_autosuspend_delay(&dev->pdev->dev, value); + pr_info("Set pm runtime auto suspend delay to %ldms\n", value); + break; + default: + pr_warn("Unsupported config!\n"); + } + +out: + return ret < 0 ? ret : count; +} + +static ssize_t encoder_config_read(struct file *filp, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct hantrovcmd_dev *dev = hantrovcmd_data; + memset(dev->config_buf, 0, VC8000E_MAX_CONFIG_LEN); + return 0; +} + +static const struct file_operations encoder_debug_ops = { + .write = encoder_config_write, + .read = encoder_config_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static int encoder_add_debugfs(struct platform_device *pdev) +{ + root_debugfs_dir = debugfs_create_dir("vc8000e",NULL); + if (!root_debugfs_dir) { + dev_err(&pdev->dev, "Failed to create vc8000e debugfs\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "Create vc8000e debugfs.\n"); + + debugfs_create_file("config", 0600, root_debugfs_dir, + hantrovcmd_data, &encoder_debug_ops); + return 0; +} + + +/*----------------------------------------- + platform register +-----------------------------------------*/ + +static const struct of_device_id hantro_of_match[] = { + { .compatible = "thead,light-vc8000e", }, + { /* sentinel */ }, +}; + +static int check_power_domain(void) +{ + struct device_node *dn = NULL; + struct property *info = NULL; + dn = of_find_node_by_name(NULL, "venc"); + if (dn != NULL) + info = of_find_property(dn, "power-domains", NULL); + pr_debug("%s, %d: power gating is %s\n", __func__, __LINE__, + (info == NULL) ? "disabled" : "enabled"); + return (info == NULL) ? 0 : 1; +} + +static int encoder_runtime_suspend(struct device *dev) +{ + struct hantrovcmd_dev *encdev = hantrovcmd_data; + + pr_debug("%s, %d: Disable clock\n", __func__, __LINE__); + + clk_disable_unprepare(encdev->cclk); + clk_disable_unprepare(encdev->aclk); + clk_disable_unprepare(encdev->pclk); + + return 0; +} + +static int encoder_runtime_resume(struct device *dev) +{ + struct hantrovcmd_dev *encdev = hantrovcmd_data; + int ret; + + ret = clk_prepare_enable(encdev->cclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable core clock\n"); + return ret; + } + + ret = clk_prepare_enable(encdev->aclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable axi clock\n"); + clk_disable_unprepare(encdev->cclk); + return ret; + } + + ret = clk_prepare_enable(encdev->pclk); + if (ret < 0) { + dev_err(dev, "could not prepare or enable apb clock\n"); + clk_disable_unprepare(encdev->cclk); + clk_disable_unprepare(encdev->aclk); + return ret; + } + + if (encdev->has_power_domains) { +#ifdef HANTROMMU_SUPPORT + MMURestore(mmu_hwregs); +#endif + vcmd_reset(); + } + pr_debug("%s, %d: Enabled clock\n", __func__, __LINE__); + + return 0; +} + +int hantroenc_vcmd_probe(struct platform_device *pdev) +{ + int i,k; + int result; + + struct resource *mem; + mem = platform_get_resource(pdev,IORESOURCE_MEM,0); + if(mem->start) + vcmd_core_array[0].vcmd_base_addr = mem->start; + vcmd_core_array[0].vcmd_irq = platform_get_irq(pdev,0); + pr_info("%s:get irq %d\n",__func__,vcmd_core_array[0].vcmd_irq); + + result = vcmd_init(pdev); + if(result) + goto err; + + total_vcmd_core_num = 1; + for (i = 0; i< total_vcmd_core_num; i++) + { + pr_info("vcmd: module init - vcmdcore[%d] addr =0x%llx\n",i, + (long long unsigned int)vcmd_core_array[i].vcmd_base_addr); + } + hantrovcmd_data = (struct hantrovcmd_dev *)vmalloc(sizeof(struct hantrovcmd_dev)*total_vcmd_core_num); + if (hantrovcmd_data == NULL) + goto err1; + memset(hantrovcmd_data,0,sizeof(struct hantrovcmd_dev)*total_vcmd_core_num); + for(k=0;kpdev = pdev; + + encoder_add_debugfs(pdev); + + hantrovcmd_data->has_power_domains = check_power_domain(); + + hantrovcmd_data->aclk = devm_clk_get(&pdev->dev, "aclk"); + if (IS_ERR(hantrovcmd_data->aclk)) { + dev_err(&pdev->dev, "failed to get axi clock\n"); + goto err; + } + + hantrovcmd_data->cclk = devm_clk_get(&pdev->dev, "cclk"); + if (IS_ERR(hantrovcmd_data->cclk)) { + dev_err(&pdev->dev, "failed to get core clock\n"); + goto err; + } + + hantrovcmd_data->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(hantrovcmd_data->pclk)) { + dev_err(&pdev->dev, "failed to get apb clock\n"); + goto err; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, VC8000E_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + if (encoder_runtime_resume(&pdev->dev)) + { + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + } + } + pm_runtime_resume_and_get(&pdev->dev); + + init_bi_list(&global_process_manager); + result = ConfigAXIFE(1); //1: normal, 2: bypass + if(result < 0) + { + vcmd_release_AXIFE_IO(); + goto err1; + } + result = ConfigMMU(); + if(result < 0) + { + vcmd_release_MMU_IO(); + goto err1; + } + result = MMU_Kernel_map(); + if(result < 0) + goto err; + for(i=0;i busy, change your config. core_id=%d\n", + hantrovcmd_data[i].vcmd_core_cfg.vcmd_irq,i); + vcmd_release_IO(); + goto err; + } + } + else + { + pr_info("vc8000_vcmd_driver: IRQ not in use!\n"); + } + } + //cmdbuf pool allocation + //init_vcmd_non_cachable_memory_allocate(); + //for cmdbuf management + cmdbuf_used_pos=0; + for(k=0;k\n", hantrovcmd_major); + + create_kernel_process_manager(); + for(i=0;idev); + pm_runtime_put_autosuspend(&pdev->dev); + + return 0; + err: + if (root_debugfs_dir) { + debugfs_remove_recursive(root_debugfs_dir); + root_debugfs_dir = NULL; + } +#ifdef HANTROMMU_SUPPORT + MMU_Kernel_unmap(); + vcmd_pool_release(pdev); +#endif + unregister_chrdev_region(hantrovcmd_devt, 1); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + err1: + if (hantrovcmd_data != NULL) + vfree(hantrovcmd_data); + pr_info("vc8000_vcmd_driver: module not inserted\n"); + return result; + +} + +static int hantroenc_vcmd_remove(struct platform_device *pdev) +{ + int i=0; + u32 result; + + if (root_debugfs_dir) { + debugfs_remove_recursive(root_debugfs_dir); + root_debugfs_dir = NULL; + } + pm_runtime_resume_and_get(&pdev->dev); + + for(i=0;idev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + encoder_runtime_suspend(&pdev->dev); + vfree(hantrovcmd_data); + + cdev_del(&hantrovcmd_cdev); + device_destroy(hantrovcmd_class, hantrovcmd_devt); + unregister_chrdev_region(hantrovcmd_devt, 1); + class_destroy(hantrovcmd_class); +#ifndef DYNAMIC_MALLOC_VCMDNODE + if (g_cmdbuf_obj_pool) { + vfree(g_cmdbuf_obj_pool); + g_cmdbuf_obj_pool = NULL; + } + if (g_cmdbuf_node_pool) { + vfree(g_cmdbuf_node_pool); + g_cmdbuf_node_pool = NULL; + } +#endif + + pr_info("vc8000_vcmd_driver: module removed\n"); + return 0; +} + + +static const struct dev_pm_ops encoder_runtime_pm_ops = { + SET_RUNTIME_PM_OPS(encoder_runtime_suspend, encoder_runtime_resume, NULL) +}; + +static struct platform_driver hantroenc_vcmd_driver = { + .probe = hantroenc_vcmd_probe, + .remove = hantroenc_vcmd_remove, + .driver = { + .name = "encoder_hantroenc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hantro_of_match), + .pm = &encoder_runtime_pm_ops, + } +}; + + +int __init hantroenc_vcmd_init(void) +{ + int ret = 0; + pr_debug("enter %s\n",__func__); + ret = platform_driver_register(&hantroenc_vcmd_driver); + if(ret) + { + pr_err("register platform driver failed!\n"); + } + return ret; +} + +void __exit hantroenc_vcmd_cleanup(void) +{ + pr_debug("enter %s\n",__func__); + platform_driver_unregister(&hantroenc_vcmd_driver); + return; +} + +static int vcmd_reserve_IO(void) +{ + u32 hwid; + int i; + u32 found_hw = 0; + + pr_info("vcmd_reserve_IO: total_vcmd_core_num is %d\n", total_vcmd_core_num); + for (i=0;i> 16) & 0xFFFF) != VCMD_HW_ID ) + { + pr_info("hantrovcmd: HW not found at 0x%llx\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr); + iounmap(( void *) hantrovcmd_data[i].hwregs); + release_mem_region(hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hantrovcmd_data[i].vcmd_core_cfg.vcmd_iosize); + hantrovcmd_data[i].hwregs = NULL; + continue; + } + + found_hw = 1; + + pr_info( + "hantrovcmd: HW at base <0x%llx> with ID <0x%08x>\n", + (long long unsigned int)hantrovcmd_data[i].vcmd_core_cfg.vcmd_base_addr, hwid); + + } + + if (found_hw == 0) + { + pr_err("hantrovcmd: NO ANY HW found!!\n"); + return -1; + } + + return 0; +} + +static void vcmd_release_IO(void) +{ + u32 i; + + vcmd_release_AXIFE_IO(); + vcmd_release_MMU_IO(); + for (i=0;ispinlock, flags); + if (dev->list_manager.head==NULL) + { + PDEBUG( "hantrovcmd_isr:received IRQ but core has nothing to do.\n"); + irq_status = vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,irq_status); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + + PDEBUG( "hantrovcmd_isr: received IRQ!\n"); + irq_status = vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + for(i=0;ihwregs, i*4); + pr_info("vcmd register %d:0x%x\n",i,fordebug); + } + } +#endif + + if(!irq_status) + { + //pr_info("hantrovcmd_isr error,irq_status :0x%x",irq_status); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + + PDEBUG( "irq_status of %d is:0x%x\n",dev->core_id,irq_status); + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,irq_status); + dev->reg_mirror[VCMD_REGISTER_INT_STATUS_OFFSET/4] = irq_status; + + if((dev->hw_version_id > HW_ID_1_0_C )&&(irq_status&0x3f)) + { + //if error,read from register directly. + cmdbuf_id = vcmd_get_register_value((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_CMDBUF_EXECUTING_ID); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else if((dev->hw_version_id > HW_ID_1_0_C )) + { + //read cmdbuf id from ddr +#ifdef VCMD_DEBUG_INTERNAL + { + u32 i, fordebug; + pr_info("ddr vcmd register phy_addr=0x%x\n",dev->vcmd_reg_mem_busAddress); + pr_info("ddr vcmd register virt_addr=0x%x\n",dev->vcmd_reg_mem_virtualAddress); + for(i=0;ivcmd_reg_mem_virtualAddress+i); + pr_info("ddr vcmd register %d:0x%x\n",i,fordebug); + } + } +#endif + + cmdbuf_id = *(dev->vcmd_reg_mem_virtualAddress+EXECUTING_CMDBUF_ID_ADDR); + pr_debug("hantrovcmd_isr: cmdbuf_id %d from virtual!!\n", cmdbuf_id); + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + + + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_RESET)) + { + //reset error,all cmdbuf that is not done will be run again. + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + //find the first run_done=0 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done == 0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + base_cmdbuf_node = new_cmdbuf_node; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + handled++; + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ABORT)) + { + //abort error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmdbuf that tigers ABORT + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + if(software_triger_abort==0) + { + //for QCFE + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + //to let high priority cmdbuf be inserted + wake_up_interruptible_all(dev->wait_abort_queue); + handled++; + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_BUSERR)) + { + //bus error, don't need to reset where to record status? + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the buserr cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_BUSERR; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_TIMEOUT)) + { + //time out,need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the timeout cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + new_cmdbuf_node = new_cmdbuf_node->previous; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //reset + vcmd_reset_current_asic(dev); + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + return IRQ_HANDLED; + } + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_CMDERR)) + { + //command error,don't need to reset + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id %d!!\n", cmdbuf_id); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + exe_cmdbuf_busAddress = VCMDGetAddrRegisterValue((const void *)dev->hwregs,dev->reg_mirror,HWIF_VCMD_EXECUTING_CMD_ADDR); + //find the cmderror cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr) <=exe_cmdbuf_busAddress)&&(((cmdbuf_obj->cmdbuf_busAddress-base_ddr_addr+cmdbuf_obj->cmdbuf_size) >exe_cmdbuf_busAddress)) ) &&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + new_cmdbuf_node = base_cmdbuf_node; + if(new_cmdbuf_node!=NULL) + { + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_CMDERR;//cmderr + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + return IRQ_HANDLED; + } + + if(vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_ENDCMD)) + { + //end command interrupt + new_cmdbuf_node = dev->list_manager.head; + dev->working_state = WORKING_STATE_IDLE; + if(dev->hw_version_id > HW_ID_1_0_C ) + { + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + else + { + //find the end cmdbuf + while(1) + { + if(new_cmdbuf_node==NULL) + { + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->has_end_cmdbuf == 1)&&(cmdbuf_obj->cmdbuf_run_done==0)) + break; + new_cmdbuf_node = new_cmdbuf_node->next; + } + } + base_cmdbuf_node = new_cmdbuf_node; + // this cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + base_cmdbuf_node=base_cmdbuf_node->next; + vcmd_delink_cmdbuf(dev,base_cmdbuf_node); + vcmd_link_cmdbuf(dev,base_cmdbuf_node); + if(dev->sw_cmdbuf_rdy_num !=0) + { + //restart new command + vcmd_start(dev,base_cmdbuf_node); + } + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + handled++; + return IRQ_HANDLED; + } + if(dev->hw_version_id <= HW_ID_1_0_C ) + cmdbuf_id = vcmd_get_register_mirror_value(dev->reg_mirror,HWIF_VCMD_IRQ_INTCMD); + if(cmdbuf_id) + { + if(dev->hw_version_id <= HW_ID_1_0_C ) + { + if(cmdbuf_id>=TOTAL_DISCRETE_CMDBUF_NUM) + { + pr_err("hantrovcmd_isr error cmdbuf_id greater than the ceiling !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + } + new_cmdbuf_node = global_cmdbuf_node[cmdbuf_id]; + if(new_cmdbuf_node==NULL) + { + pr_err("hantrovcmd_isr error cmdbuf_id !!\n"); + spin_unlock_irqrestore(dev->spinlock, flags); + return IRQ_HANDLED; + } + // interrupt cmdbuf and cmdbufs prior to itself, run_done = 1 + while(1) + { + if(new_cmdbuf_node==NULL) + break; + cmdbuf_obj = (struct cmdbuf_obj*)new_cmdbuf_node->data; + if((cmdbuf_obj->cmdbuf_run_done==0)) + { + cmdbuf_obj->cmdbuf_run_done=1; + cmdbuf_obj->executing_status = CMDBUF_EXE_STATUS_OK; + cmdbuf_processed_num++; + } + else + break; + new_cmdbuf_node = new_cmdbuf_node->previous; + } + handled++; + } + + spin_unlock_irqrestore(dev->spinlock, flags); + if(cmdbuf_processed_num) + wake_up_interruptible_all(dev->wait_queue); + if(!handled) + { + PDEBUG("IRQ received, but not hantro's!\n"); + } + return IRQ_HANDLED; +} + +static void vcmd_reset_asic(struct hantrovcmd_dev * dev) +{ + int i,n; + u32 result; + for (n=0;nhwregs!=NULL) + { + //disable interrupt at first + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_CTL_OFFSET,0x0000); + //reset all + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_CONTROL_OFFSET,0x0002); + //read status register + result =vcmd_read_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET); + //clean status register + vcmd_write_reg((const void *)dev->hwregs,VCMD_REGISTER_INT_STATUS_OFFSET,result); + } + +} + +#ifdef VCMD_DEBUG_INTERNAL +static void printk_vcmd_register_debug(const void *hwregs, char * info) +{ + u32 i, fordebug; + for(i=0;i= vcmd_buf_mem_pool.busAddress && + (addr - vcmd_buf_mem_pool.busAddress + size) <= CMDBUF_POOL_TOTAL_SIZE) || + (addr >= vcmd_status_buf_mem_pool.busAddress && + (addr - vcmd_status_buf_mem_pool.busAddress + size) <= CMDBUF_POOL_TOTAL_SIZE) || + (addr >= vcmd_registers_mem_pool.busAddress && + (addr - vcmd_status_buf_mem_pool.busAddress + size) <= CMDBUF_VCMD_REGISTER_TOTAL_SIZE); + + return bInRange; +} diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregisterenum.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregisterenum.h new file mode 100644 index 000000000..cb9addf5f --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregisterenum.h @@ -0,0 +1,157 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/* Register interface based on the document version 1.1.2 */ + HWIF_VCMD_HW_ID, + HWIF_VCMD_HW_VERSION, + HWIF_VCMD_HW_BUILD_DATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE, + HWIF_VCMD_EXE_CMDBUF_COUNT, + HWIF_VCMD_EXECUTING_CMD, + HWIF_VCMD_EXECUTING_CMD_MSB, + HWIF_VCMD_AXI_TOTAL_AR_LEN, + HWIF_VCMD_AXI_TOTAL_R, + HWIF_VCMD_AXI_TOTAL_AR, + HWIF_VCMD_AXI_TOTAL_R_LAST, + HWIF_VCMD_AXI_TOTAL_AW_LEN, + HWIF_VCMD_AXI_TOTAL_W, + HWIF_VCMD_AXI_TOTAL_AW, + HWIF_VCMD_AXI_TOTAL_W_LAST, + HWIF_VCMD_AXI_TOTAL_B, + HWIF_VCMD_AXI_AR_VALID, + HWIF_VCMD_AXI_AR_READY, + HWIF_VCMD_AXI_R_VALID, + HWIF_VCMD_AXI_R_READY, + HWIF_VCMD_AXI_AW_VALID, + HWIF_VCMD_AXI_AW_READY, + HWIF_VCMD_AXI_W_VALID, + HWIF_VCMD_AXI_W_READY, + HWIF_VCMD_AXI_B_VALID, + HWIF_VCMD_AXI_B_READY, + HWIF_VCMD_WORK_STATE, + HWIF_VCMD_INIT_MODE, + HWIF_VCMD_AXI_CLK_GATE_DISABLE, + HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE, + HWIF_VCMD_CORE_CLK_GATE_DISABLE, + HWIF_VCMD_ABORT_MODE, + HWIF_VCMD_RESET_CORE, + HWIF_VCMD_RESET_ALL, + HWIF_VCMD_START_TRIGGER, + HWIF_VCMD_IRQ_INTCMD, + HWIF_VCMD_IRQ_JMPP, + HWIF_VCMD_IRQ_JMPD, + HWIF_VCMD_IRQ_RESET, + HWIF_VCMD_IRQ_ABORT, + HWIF_VCMD_IRQ_CMDERR, + HWIF_VCMD_IRQ_TIMEOUT, + HWIF_VCMD_IRQ_BUSERR, + HWIF_VCMD_IRQ_ENDCMD, + HWIF_VCMD_IRQ_INTCMD_EN, + HWIF_VCMD_IRQ_JMPP_EN, + HWIF_VCMD_IRQ_JMPD_EN, + HWIF_VCMD_IRQ_RESET_EN, + HWIF_VCMD_IRQ_ABORT_EN, + HWIF_VCMD_IRQ_CMDERR_EN, + HWIF_VCMD_IRQ_TIMEOUT_EN, + HWIF_VCMD_IRQ_BUSERR_EN, + HWIF_VCMD_IRQ_ENDCMD_EN, + HWIF_VCMD_TIMEOUT_EN, + HWIF_VCMD_TIMEOUT_CYCLES, + HWIF_VCMD_EXECUTING_CMD_ADDR, + HWIF_VCMD_EXECUTING_CMD_ADDR_MSB, + HWIF_VCMD_EXE_CMDBUF_LENGTH, + HWIF_VCMD_CMD_SWAP, + HWIF_VCMD_MAX_BURST_LEN, + HWIF_VCMD_AXI_ID_RD, + HWIF_VCMD_AXI_ID_WR, + HWIF_VCMD_RDY_CMDBUF_COUNT, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCD_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_GATE, + HWIF_VCMD_EXT_ABN_INT_SRC_VCE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCD_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_GATE, + HWIF_VCMD_EXT_NORM_INT_SRC_VCE_GATE, + HWIF_VCMD_CMDBUF_EXECUTING_ID, diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregistertable.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregistertable.h new file mode 100644 index 000000000..4629fcdc1 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdregistertable.h @@ -0,0 +1,157 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/* Register interface based on the document version 1.1.2 */ + VCMDREG(HWIF_VCMD_HW_ID , 0 ,0xffff0000, 16, 0,RO,"HW ID"), + VCMDREG(HWIF_VCMD_HW_VERSION , 0 ,0x0000ffff, 0, 0,RO,"version of hw(1.0.0).[15:12]-major [11:8]-minor [7:0]-build"), + VCMDREG(HWIF_VCMD_HW_BUILD_DATE , 4 ,0xffffffff, 0, 0,RO,"Hw package generation date in BCD code"), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU , 8 ,0x08000000, 27, 0,RO,"external abnormal interrupt source from mmu of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE , 8 ,0x04000000, 26, 0,RO,"external abnormal interrupt source from l2cache of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400 , 8 ,0x02000000, 25, 0,RO,"external abnormal interrupt source from dec400 of vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD , 8 ,0x01000000, 24, 0,RO,"external abnormal interrupt source from vcd."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU , 8 ,0x00200000, 21, 0,RO,"external abnormal interrupt source from mmu of cutree."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU , 8 ,0x00100000, 20, 0,RO,"external abnormal interrupt source from mmu of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE , 8 ,0x00080000, 19, 0,RO,"external abnormal interrupt source from l2 cache of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400 , 8 ,0x00040000, 18, 0,RO,"external abnormal interrupt source from dec400 of vce."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE , 8 ,0x00020000, 17, 0,RO,"external abnormal interrupt source from cutree."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE , 8 ,0x00010000, 16, 0,RO,"external abnormal interrupt source from vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU , 8 ,0x00000800, 11, 0,RO,"external normal interrupt source from mmu of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE , 8 ,0x00000400, 10, 0,RO,"external normal interrupt source from l2cache of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400 , 8 ,0x00000200, 9, 0,RO,"external normal interrupt source from dec400 of vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD , 8 ,0x00000100, 8, 0,RO,"external normal interrupt source from vcd."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU , 8 ,0x00000020, 5, 0,RO,"external normal interrupt source from mmu of cutree."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU , 8 ,0x00000010, 4, 0,RO,"external normal interrupt source from mmu of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE , 8 ,0x00000008, 3, 0,RO,"external normal interrupt source from l2 cache of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400 , 8 ,0x00000004, 2, 0,RO,"external normal interrupt source from dec400 of vce."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE , 8 ,0x00000002, 1, 0,RO,"external normal interrupt source from cutree."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE , 8 ,0x00000001, 0, 0,RO,"external normal interrupt source from vce."), + VCMDREG(HWIF_VCMD_EXE_CMDBUF_COUNT , 12 ,0xffffffff, 0, 0,RO,"Hw increases this counter by 1 after one more command buffer has been executed"), + VCMDREG(HWIF_VCMD_EXECUTING_CMD , 16 ,0xffffffff, 0, 0,RO,"the first 32 bits of the executing cmd."), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_MSB , 20 ,0xffffffff, 0, 0,RO,"the second 32 bits of the executing cmd."), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AR_LEN , 24 ,0xffffffff, 0, 0,RO,"axi total ar length"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_R , 28 ,0xffffffff, 0, 0,RO,"axi total r"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AR , 32 ,0xffffffff, 0, 0,RO,"axi total ar"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_R_LAST , 36 ,0xffffffff, 0, 0,RO,"axi total r last"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AW_LEN , 40 ,0xffffffff, 0, 0,RO,"axi total aw length"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_W , 44 ,0xffffffff, 0, 0,RO,"axi total w"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_AW , 48 ,0xffffffff, 0, 0,RO,"axi total aw"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_W_LAST , 52 ,0xffffffff, 0, 0,RO,"axi total w last"), + VCMDREG(HWIF_VCMD_AXI_TOTAL_B , 56 ,0xffffffff, 0, 0,RO,"axi total b"), + VCMDREG(HWIF_VCMD_AXI_AR_VALID , 60 ,0x80000000, 31, 0,RO,"axi ar valid"), + VCMDREG(HWIF_VCMD_AXI_AR_READY , 60 ,0x40000000, 30, 0,RO,"axi ar ready"), + VCMDREG(HWIF_VCMD_AXI_R_VALID , 60 ,0x20000000, 29, 0,RO,"axi r valid"), + VCMDREG(HWIF_VCMD_AXI_R_READY , 60 ,0x10000000, 28, 0,RO,"axi r ready"), + VCMDREG(HWIF_VCMD_AXI_AW_VALID , 60 ,0x08000000, 27, 0,RO,"axi aw valid"), + VCMDREG(HWIF_VCMD_AXI_AW_READY , 60 ,0x04000000, 26, 0,RO,"axi aw ready"), + VCMDREG(HWIF_VCMD_AXI_W_VALID , 60 ,0x02000000, 25, 0,RO,"axi w valid"), + VCMDREG(HWIF_VCMD_AXI_W_READY , 60 ,0x01000000, 24, 0,RO,"axi w ready"), + VCMDREG(HWIF_VCMD_AXI_B_VALID , 60 ,0x00800000, 23, 0,RO,"axi b valid"), + VCMDREG(HWIF_VCMD_AXI_B_READY , 60 ,0x00400000, 22, 0,RO,"axi b ready"), + VCMDREG(HWIF_VCMD_WORK_STATE , 60 ,0x00000007, 0, 0,RO,"hw work state. 0-IDLE 1-WORK 2-STALL 3-PEND 4-ABORT"), + VCMDREG(HWIF_VCMD_INIT_MODE , 64 ,0x00000080, 7, 0,RW,"After executed a END command in init mode, VCMD will get back to normal mode"), + VCMDREG(HWIF_VCMD_AXI_CLK_GATE_DISABLE , 64 ,0x00000040, 6, 0,RW,"keep axi_clk always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_MASTER_OUT_CLK_GATE_DISABLE , 64 ,0x00000020, 5, 0,RW,"keep master_out_clk(APB/AHB master) always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_CORE_CLK_GATE_DISABLE , 64 ,0x00000010, 4, 0,RW,"keep core_clk always on when this bit is set to 1"), + VCMDREG(HWIF_VCMD_ABORT_MODE , 64 ,0x00000008, 3, 0,RW,"0:abort after finishing current cmdbuf command.1:abort immediately "), + VCMDREG(HWIF_VCMD_RESET_CORE , 64 ,0x00000004, 2, 0,RW,"sw write 1 to this bit will rset HW core logic when AXI/APB bus is idle."), + VCMDREG(HWIF_VCMD_RESET_ALL , 64 ,0x00000002, 1, 0,RW,"sw write 1 to this bit will rset HW immediately including all swregs and AXI/APB bus logic"), + VCMDREG(HWIF_VCMD_START_TRIGGER , 64 ,0x00000001, 0, 0,RW,"0:abort previou task and stop hw. 1:trigger hw to fetch and execute commands."), + VCMDREG(HWIF_VCMD_IRQ_INTCMD , 68 ,0xffff0000, 16, 0,RW,"interrupt sources which are triggered by command buffer id.. Only for version 1.0.c"), + VCMDREG(HWIF_VCMD_IRQ_JMPP , 68 ,0x00000080, 7, 0,RW,"interrupt source which is triggered by JMP command when hw goes to PEND state."), + VCMDREG(HWIF_VCMD_IRQ_JMPD , 68 ,0x00000040, 6, 0,RW,"interrupt source which is triggered by JMP command directly."), + VCMDREG(HWIF_VCMD_IRQ_RESET , 68 ,0x00000020, 5, 0,RW,"interrupt source which is triggered by hw reset or sw_vcmd_reset_all."), + VCMDREG(HWIF_VCMD_IRQ_ABORT , 68 ,0x00000010, 4, 0,RW,"interrupt source which is triggered by abort operation."), + VCMDREG(HWIF_VCMD_IRQ_CMDERR , 68 ,0x00000008, 3, 0,RW,"interrupt source which is triggered when there is illegal command in cmdbuf"), + VCMDREG(HWIF_VCMD_IRQ_TIMEOUT , 68 ,0x00000004, 2, 0,RW,"interrupt source which is triggered when vcmd timeout."), + VCMDREG(HWIF_VCMD_IRQ_BUSERR , 68 ,0x00000002, 1, 0,RW,"interrupt source which is triggered when there is bus error."), + VCMDREG(HWIF_VCMD_IRQ_ENDCMD , 68 ,0x00000001, 0, 0,RW,"interrupt source which is triggered by END command."), + VCMDREG(HWIF_VCMD_IRQ_INTCMD_EN , 72 ,0xffff0000, 16, 0,RW,"interrupt sources which are triggered by command buffer id. Only for version 1.0.c"), + VCMDREG(HWIF_VCMD_IRQ_JMPP_EN , 72 ,0x00000080, 7, 0,RW,"interrupt enable for sw_vcmd_irq_jmpp"), + VCMDREG(HWIF_VCMD_IRQ_JMPD_EN , 72 ,0x00000040, 6, 0,RW,"interrupt enable for sw_vcmd_irq_jmpd"), + VCMDREG(HWIF_VCMD_IRQ_RESET_EN , 72 ,0x00000020, 5, 0,RW,"interrupt enable for sw_vcmd_irq_reset"), + VCMDREG(HWIF_VCMD_IRQ_ABORT_EN , 72 ,0x00000010, 4, 0,RW,"interrupt enable for sw_vcmd_irq_abort"), + VCMDREG(HWIF_VCMD_IRQ_CMDERR_EN , 72 ,0x00000008, 3, 0,RW,"interrupt enable for sw_vcmd_irq_cmderr"), + VCMDREG(HWIF_VCMD_IRQ_TIMEOUT_EN , 72 ,0x00000004, 2, 0,RW,"interrupt enable for sw_vcmd_irq_timeout"), + VCMDREG(HWIF_VCMD_IRQ_BUSERR_EN , 72 ,0x00000002, 1, 0,RW,"interrupt enable for sw_vcmd_irq_buserr"), + VCMDREG(HWIF_VCMD_IRQ_ENDCMD_EN , 72 ,0x00000001, 0, 0,RW,"interrupt enable for sw_vcmd_irq_endcmd"), + VCMDREG(HWIF_VCMD_TIMEOUT_EN , 76 ,0x80000000, 31, 0,RW,"1:timeout work. 0: timeout do not work"), + VCMDREG(HWIF_VCMD_TIMEOUT_CYCLES , 76 ,0x7fffffff, 0, 0,RW,"sw_vcmd_irq_timeout will be generated when timeout counter is equal to this value."), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_ADDR , 80 ,0xffffffff, 0, 0,RW,"the least 32 bits address of the executing command"), + VCMDREG(HWIF_VCMD_EXECUTING_CMD_ADDR_MSB , 84 ,0xffffffff, 0, 0,RW,"the most 32 bits address of the executing command"), + VCMDREG(HWIF_VCMD_EXE_CMDBUF_LENGTH , 88 ,0x0000ffff, 0, 0,RW,"the length of current command buffer in unit of 64bits."), + VCMDREG(HWIF_VCMD_CMD_SWAP , 92 ,0xf0000000, 28, 0,RW,"axi data swapping"), + VCMDREG(HWIF_VCMD_MAX_BURST_LEN , 92 ,0x00ff0000, 16, 0,RW,"max burst length which will be sent to axi bus"), + VCMDREG(HWIF_VCMD_AXI_ID_RD , 92 ,0x0000ff00, 8, 0,RW,"the arid which will be used on axi bus reading"), + VCMDREG(HWIF_VCMD_AXI_ID_WR , 92 ,0x000000ff, 0, 0,RW,"the awid which will be used on axi bus writing"), + VCMDREG(HWIF_VCMD_RDY_CMDBUF_COUNT , 96 ,0xffffffff, 0, 0,RW,"sw increases this counter by 1 after one more command buffer was ready."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_MMU_GATE , 100,0x10000000, 28, 0,RW,"external abnormal interrupt source from mmu of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_L2CACHE_GATE, 100,0x08000000, 27, 0,RW,"external abnormal interrupt source from l2cache of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_DEC400_GATE, 100,0x04000000, 26, 0,RW,"external abnormal interrupt source from dec400 of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCD_GATE , 100,0x01000000, 24, 0,RW,"external abnormal interrupt source from vcd gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_MMU_GATE, 100,0x00200000, 21, 0,RW,"external abnormal interrupt source from mmu of cutree gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_MMU_GATE , 100,0x00100000, 20, 0,RW,"external abnormal interrupt source from mmu of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_L2CACHE_GATE, 100,0x00080000, 19, 0,RW,"external abnormal interrupt source from l2 cache of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_DEC400_GATE, 100,0x00040000, 18, 0,RW,"external abnormal interrupt source from dec400 of vce gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_CUTREE_GATE , 100,0x00020000, 17, 0,RW,"external abnormal interrupt source from cutree gate."), + VCMDREG(HWIF_VCMD_EXT_ABN_INT_SRC_VCE_GATE , 100,0x00010000, 16, 0,RW,"external abnormal interrupt source from vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_MMU_GATE , 100,0x00000800, 11, 0,RW,"external normal interrupt source from mmu of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_L2CACHE_GATE, 100,0x00000400, 10, 0,RW,"external normal interrupt source from l2cache of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_DEC400_GATE, 100,0x00000200, 9, 0,RW,"external normal interrupt source from dec400 of vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCD_GATE , 100,0x00000100, 8, 0,RW,"external normal interrupt source from vcd gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_MMU_GATE, 100,0x00000020, 5, 0,RW,"external normal interrupt source from mmu of cutree gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_MMU_GATE , 100,0x00000010, 4, 0,RW,"external normal interrupt source from mmu of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_L2CACHE_GATE, 100,0x00000008, 3, 0,RW,"external normal interrupt source from l2 cache of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_DEC400_GATE, 100,0x00000004, 2, 0,RW,"external normal interrupt source from dec400 of vce gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_CUTREE_GATE , 100,0x00000002, 1, 0,RW,"external normal interrupt source from cutree gate."), + VCMDREG(HWIF_VCMD_EXT_NORM_INT_SRC_VCE_GATE , 100,0x00000001, 0, 0,RW,"external normal interrupt source from vce gate."), + VCMDREG(HWIF_VCMD_CMDBUF_EXECUTING_ID , 104,0xffffffff, 0, 0,RW,"The ID of current executing command buffer.used after version 1.1.2."), diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.c b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.c new file mode 100644 index 000000000..da89851cd --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.c @@ -0,0 +1,180 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ +/*------------------------------------------------------------------------------ + + Table of contents + + 1. Include headers + 2. External compiler flags + 3. Module defines + +------------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------------ + 1. Include headers +------------------------------------------------------------------------------*/ + +#include +#include "vcmdswhwregisters.h" + +/* NOTE: Don't use ',' in descriptions, because it is used as separator in csv + * parsing. */ +const regVcmdField_s asicVcmdRegisterDesc[] = +{ +#include "vcmdregistertable.h" +}; + +/*------------------------------------------------------------------------------ + 2. External compiler flags +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- + 3. Module defines +------------------------------------------------------------------------------*/ + +/* Define this to print debug info for every register write. +#define DEBUG_PRINT_REGS */ + +/******************************************************************************* + Function name : vcmd_read_reg + Description : Retrive the content of a hadware register + Note: The status register will be read after every MB + so it may be needed to buffer it's content if reading + the HW register is slow. + Return type : u32 + Argument : u32 offset +*******************************************************************************/ +u32 vcmd_read_reg(const void *hwregs, u32 offset) +{ + u32 val; + + val =(u32) ioread32((void*)hwregs + offset); + + PDEBUG("vcmd_read_reg 0x%02x --> %08x\n", offset, val); + + return val; +} + +/******************************************************************************* + Function name : vcmd_write_reg + Description : Set the content of a hadware register + Return type : void + Argument : u32 offset + Argument : u32 val +*******************************************************************************/ +void vcmd_write_reg(const void *hwregs, u32 offset, u32 val) +{ + iowrite32(val,(void*)hwregs + offset); + + PDEBUG("vcmd_write_reg 0x%02x with value %08x\n", offset, val); +} + + +/*------------------------------------------------------------------------------ + + vcmd_write_register_value + + Write a value into a defined register field (write will happens actually). + +------------------------------------------------------------------------------*/ +void vcmd_write_register_value(const void *hwregs,u32* reg_mirror,regVcmdName name, u32 value) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + +#ifdef DEBUG_PRINT_REGS + PDEBUG("vcmd_write_register_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, value, field->description); +#endif + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("(field->mask >> field->lsb) >= value=%d\n",(field->mask >> field->lsb) >= value); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT*4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT*4); + + /* Clear previous value of field in register */ + regVal = reg_mirror[field->base/4] & ~(field->mask); + + /* Put new value of field in register */ + reg_mirror[field->base/4] = regVal | ((value << field->lsb) & field->mask); + + /* write it into HW registers */ + vcmd_write_reg(hwregs, field->base,reg_mirror[field->base/4]); +} + +/*------------------------------------------------------------------------------ + + vcmd_get_register_value + + Get an unsigned value from the ASIC registers + +------------------------------------------------------------------------------*/ +u32 vcmd_get_register_value(const void *hwregs, u32* reg_mirror,regVcmdName name) +{ + const regVcmdField_s *field; + u32 value; + + field = &asicVcmdRegisterDesc[name]; + + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + value = reg_mirror[field->base / 4] = vcmd_read_reg(hwregs, field->base); + value = (value & field->mask) >> field->lsb; + + return value; +} + + diff --git a/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.h b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.h new file mode 100644 index 000000000..0f5719994 --- /dev/null +++ b/drivers/staging/media/vpu-vc8000e-kernel/linux/kernel_module/vcmdswhwregisters.h @@ -0,0 +1,244 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2021 VERISILICON +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2021 VERISILICON +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + +/*------------------------------------------------------------------------------ + + Table of contents + + 1. Include headers + 2. External compiler flags + 3. Module defines + +------------------------------------------------------------------------------*/ +#ifndef VCMD_SWHWREGISTERS_H +#define VCMD_SWHWREGISTERS_H + +#ifdef __cplusplus +extern "C" { +#endif +/*------------------------------------------------------------------------------ + 1. Include headers +------------------------------------------------------------------------------*/ +#ifdef __FREERTOS__ +#include "base_type.h" +#include "io_tools.h" +#elif defined(__linux__) +#include +#include +#include +#endif + +#ifdef __FREERTOS__ +//ptr_t has been defined in base_type.h //Now the FreeRTOS mem need to support 64bit env +#elif defined(__linux__) +typedef int i32; +typedef size_t ptr_t; +#endif + +#undef PDEBUG /* undef it, just in case */ +#ifdef REGISTER_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_INFO "memalloc: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/*------------------------------------------------------------------------------ + 2. External compiler flags +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- + 3. Module defines +------------------------------------------------------------------------------*/ +#ifdef HANTROVCMD_ENABLE_IP_SUPPORT +#define VCMD_REGISTER_INDEX_SW_INIT_CMD0 32 +#define ASIC_VCMD_SWREG_AMOUNT 64 +#else +#define ASIC_VCMD_SWREG_AMOUNT 27 +#endif +#define VCMD_REGISTER_CONTROL_OFFSET 0X40 +#define VCMD_REGISTER_INT_STATUS_OFFSET 0X44 +#define VCMD_REGISTER_INT_CTL_OFFSET 0X48 +#define VCMD_REGISTER_EXT_INT_GATE_OFFSET 0X64 +/* HW Register field names */ +typedef enum +{ +#include "vcmdregisterenum.h" + VcmdRegisterAmount +} regVcmdName; + +/* HW Register field descriptions */ +typedef struct +{ + u32 name; /* Register name and index */ + i32 base; /* Register base address */ + u32 mask; /* Bitmask for this field */ + i32 lsb; /* LSB for this field [31..0] */ + i32 trace; /* Enable/disable writing in swreg_params.trc */ + i32 rw; /* 1=Read-only 2=Write-only 3=Read-Write */ + char *description; /* Field description */ +} regVcmdField_s; + +/* Flags for read-only, write-only and read-write */ +#define RO 1 +#define WO 2 +#define RW 3 + +#define REGBASE(reg) (asicVcmdRegisterDesc[reg].base) + +/* Description field only needed for system model build. */ +#ifdef TEST_DATA +#define VCMDREG(name, base, mask, lsb, trace, rw, desc) \ + {name, base, mask, lsb, trace, rw, desc} +#else +#define VCMDREG(name, base, mask, lsb, trace, rw, desc) \ + {name, base, mask, lsb, trace, rw, ""} +#endif + + +/*------------------------------------------------------------------------------ + 4. Function prototypes +------------------------------------------------------------------------------*/ +extern const regVcmdField_s asicVcmdRegisterDesc[]; + +/*------------------------------------------------------------------------------ + + EncAsicSetRegisterValue + + Set a value into a defined register field + +------------------------------------------------------------------------------*/ +static inline void vcmd_set_register_mirror_value(u32 *reg_mirror, regVcmdName name, u32 value) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + +#ifdef DEBUG_PRINT_REGS + printf("vcmd_set_register_mirror_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, value, field->description); +#endif + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("(field->mask >> field->lsb) >= value=%d\n",(field->mask >> field->lsb) >= value); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + /* Clear previous value of field in register */ + regVal = reg_mirror[field->base / 4] & ~(field->mask); + + /* Put new value of field in register */ + reg_mirror[field->base / 4] = regVal | ((value << field->lsb) & field->mask); +} +static inline u32 vcmd_get_register_mirror_value(u32 *reg_mirror, regVcmdName name) +{ + const regVcmdField_s *field; + u32 regVal; + + field = &asicVcmdRegisterDesc[name]; + + + /* Check that value fits in field */ + PDEBUG("field->name == name=%d\n",field->name == name); + PDEBUG("((field->mask >> field->lsb) << field->lsb) == field->mask=%d\n",((field->mask >> field->lsb) << field->lsb) == field->mask); + PDEBUG("field->base < ASIC_VCMD_SWREG_AMOUNT * 4=%d\n",field->base < ASIC_VCMD_SWREG_AMOUNT * 4); + + regVal = reg_mirror[field->base / 4]; + regVal = (regVal & field->mask) >> field->lsb; + +#ifdef DEBUG_PRINT_REGS + PDEBUG("vcmd_get_register_mirror_value 0x%2x 0x%08x Value: %10d %s\n", + field->base, field->mask, regVal, field->description); +#endif + return regVal; +} + +u32 vcmd_read_reg(const void *hwregs, u32 offset); + +void vcmd_write_reg(const void *hwregs, u32 offset, u32 val); + + + +void vcmd_write_register_value(const void *hwregs,u32* reg_mirror,regVcmdName name, u32 value); + + +u32 vcmd_get_register_value(const void *hwregs, u32* reg_mirror,regVcmdName name); + +#define vcmd_set_addr_register_value(reg_base, reg_mirror, name, value) do {\ + if(sizeof(ptr_t) == 8) {\ + vcmd_write_register_value((reg_base), (reg_mirror),name, (u32)((ptr_t)value)); \ + vcmd_write_register_value((reg_base), (reg_mirror),name##_MSB, (u32)(((ptr_t)value) >> 32));\ + } else {\ + vcmd_write_register_value((reg_base),(reg_mirror), name, (u32)((ptr_t)value));\ + }\ +}while (0) + +#define VCMDGetAddrRegisterValue(reg_base, reg_mirror,name) \ + ((sizeof(ptr_t) == 8) ? (\ + (((ptr_t)vcmd_get_register_value((reg_base),(reg_mirror), name)) | \ + (((ptr_t)vcmd_get_register_value((reg_base), (reg_mirror),name##_MSB)) << 32))\ + ) : ((ptr_t)vcmd_get_register_value((reg_base),(reg_mirror), (name)))) + +#ifdef __cplusplus +} +#endif + +#endif /* VCMD_SWHWREGISTERS_H */ \ No newline at end of file