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