pax_global_header 0000666 0000000 0000000 00000000064 15060370513 0014512 g ustar 00root root 0000000 0000000 52 comment=084c518866bafb71de5888405c6131e380c07ce5
snd-hdspe-1.0.2/ 0000775 0000000 0000000 00000000000 15060370513 0013377 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/.gitignore 0000664 0000000 0000000 00000000360 15060370513 0015366 0 ustar 00root root 0000000 0000000 # OS files
.DS_Store
# Temp and object files
deps
modules.order
Module.symvers
*.cmd
.module-common.o
sound/pci/hdsp/hdspe/*.o
sound/pci/hdsp/hdspe/*.ko
sound/pci/hdsp/hdspe/*.mod
sound/pci/hdsp/hdspe/*.mod.c
# IDE files
.idea/
.vscode/
snd-hdspe-1.0.2/LICENSE 0000664 0000000 0000000 00000104515 15060370513 0014412 0 ustar 00root root 0000000 0000000 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
.
snd-hdspe-1.0.2/Makefile 0000664 0000000 0000000 00000002062 15060370513 0015037 0 ustar 00root root 0000000 0000000 obj-m += sound/pci/hdsp/
# The runtime of DKMS has this environment variable to build for several versions of Linux kernel.
ifndef KERNELRELEASE
KERNELRELEASE := $(shell uname -r)
endif
KDIR ?= /lib/modules/${KERNELRELEASE}/build
PWD := $(shell pwd)
EXTRA_CFLAGS += -DDEBUG -DCONFIG_SND_DEBUG
# Force to build the module as loadable kernel module.
# Keep in mind that this configuration sound be in 'sound/pci/Kconfig' when upstreaming.
export CONFIG_SND_HDSPE=m
default: depend
$(MAKE) W=1 -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) W=1 -C $(KDIR) M=$(PWD) clean
-rm *~
-touch deps
insert: default
-rmmod snd-hdspm
insmod sound/pci/hdsp/hdspe/snd-hdspe.ko
remove:
rmmod snd-hdspe
install: default
-rmmod snd-hdspm
-ln -s $(pwd) /usr/src/alsa-hdspe-0.0
dkms install alsa-hdspe/0.0
uninstall:
dkms remove alsa-hdspe/0.0 --all
list-controls:
-rm asound.state
alsactl -f asound.state store
show-controls: list-controls
less asound.state
enable-debug-log:
echo 8 > /proc/sys/kernel/printk
depend:
gcc -MM sound/pci/hdsp/hdspe/hdspe*.c > deps
snd-hdspe-1.0.2/README.md 0000664 0000000 0000000 00000013173 15060370513 0014663 0 ustar 00root root 0000000 0000000 # snd-hdspe
New linux kernel ALSA driver for [RME](http://www.rme-audio.com) HDSPe MADI / AES / RayDAT / AIO and AIO Pro sound cards and extension modules.
Forked from [snd-hdspe](https://github.com/PhilippeBekaert/snd-hdspe) which is no longer maintained as Prof. Dr. Philippe Bekaert passed away in 2022.
This fork's `main` branch contains kernel compatibility updates for newer kernels (v5.12, v6.2). The `develop` branch contains WIP features like suspend/resume support, improved logging (debug/production levels) and fixes various compiler warnings.
**Original README text**
New linux kernel ALSA driver for [RME](http://www.rme-audio.com) HDSPe MADI / AES / RayDAT / AIO and AIO Pro sound cards and extension modules.
In addition to the functionality offered by the stock snd-hdspm linux driver, this driver provides support for the HDSPe AIO Pro card and TCO module LTC output and fixes issues such as double/quad speed support of AIO cards. Programmers will appreciate the completely updated control interface, enabling access to the advanced features of these professional audio cards though generic ALSA control mechanisms instead of ad-hoc ioctl's. Users will appreciate [hdspeconf](https://github.com/PhilippeBekaert/hdspeconf), the friendly user space HDSPe sound card configuration tool that builds upon this driver. The driver is compatible with the stock linux ALSA hdspmixer application for controlling the hardware mixer.
**Supported hardware**
- The snd-hdspe driver focusses on the current (2021) range of RME HDSPe PCIe cards, except MADI-FX: MADI, AES, RayDAT, AIO Pro. AIO is supported as well.
- The RME HDSPe MADI-FX is a different beast and is not supported by this driver. See
[Adrian Knoths MADI-FX driver work in progress](https://github.com/adiknoth/madifx).
**Building the driver**
The driver is provided here in source code, to be compiled out of kernel tree.
- Install the kernel headers and software development toolchain for your linux distribution.
On ubuntu:
sudo apt-get install linux-headers-$(uname -r)
- Clone this repository. cd to your clone copy folder and type
make
This builds the snd-hdspe.ko kernel driver in the sound/pci/hdsp/hdspe subdirectory.
- You may need to blacklist the stock snd-hdspm driver as this driver targets the same devices. Create a file /usr/lib/modprobe.d/hdspe.conf with the following content:
blacklist snd-hdspm
**Trying out the driver**
- Manually installing the snd-hdspe.ko driver: If installed, remove the snd-hdspm.ko driver (the old driver for RME HDSPe cards) first:
sudo -s
rmmod snd-hdspm
insmod sound/pci/hdsp/hdspe/snd-hdspe.ko
or
sudo make insert
You need to stop all (audio) applications using the snd-hdspm driver before, in particular PulseAudio and the jack audio server.
When manually inserting a non-signed kernel module like this, you may need to disable secure boot in your systems BIOS.
See below for how to install the kernel module using DKMS. Installing with DKMS
is preferred if you plan to use this module on a regular basis instead of just
testing it once.
- In case you would love or need to see the debug messages spit out by the snd-hdspe.ko module, enable debug log output:
sudo echo 8 > /proc/sys/kernel/printk
or
sudo make enable-debug-log
- Removing the snd-hdspe.ko driver and re-installing the default snd-hdspm driver:
sudo -s
rmmod snd-hdspe.ko
modprobe snd-hdspm
or
sude make remove
- Viewing ALSA controls:
alsactl -f asound.state store
less asound.state
or
make show-controls
- Cleaning up your repository clone folder:
make clean
**Installing the driver with DKMS build**
[Dynamic Kernel Module System (DKMS)](https://github.com/dell/dkms) makes it easy to maintain
out-of-tree kernel modules. It's preferable to use DKMS instead of manually installation since it
assists module signing for secure boot.
- Install DKMS package. On Ubuntu:
sudo apt install dkms
- For preparation, execute cd to your clone copy folder, then type
sudo ln -s $(pwd) /usr/src/alsa-hdspe-0.0
- For installing, type
sudo dkms install alsa-hdspe/0.0
- For uninstalling, type
sudo dkms remove alsa-hdspe/0.0
- Or simply:
make install
or
make uninstall
**Documentation**
- [ALSA control elements provided by this driver](doc/controls.md)
**Status**
At this time (March, 30 2022), the driver is still work in progress.
- AES, AIO, AIO Pro, MADI, RayDAT and TCO control and PCM capture and playback, and MIDI
support is ready for beta testing.
- It is developed on ubuntu studio 20.04 and has only been tested on that distribution so far.
- Other TODOs: mixer interface, loopback, power suspend and resume, ...
As the code is still work in progress, the control interface may change at
any time and features may be added or updated. Inform the author if you
are relying on the control interface or features as they are now.
**Acknowledgements**
- This work builds on previous work by the hdspm linux kernel driver authors over the last nearly 20 years:
Winfried Ritsch, Paul Davis, Marcus Andersson, Thomas Charbonnel, Remy Bruno, Florian Faber and Adrian Knoth.
(Blame [the author](mailto:linux@panokkel.be) for bugs in the new driver.)
- Thanks to [RME](http://www.rme-audio.com) for providing the necessary information and code for writing this driver.
- Thanks to [Amptec Belgium](http://www.amptec.be) for hardware support.
**License and (No) Warranty**
See [LICENSE](https://github.com/PhilippeBekaert/snd-hdspe/blob/main/LICENSE).
**Author**
[Philippe Bekaert](mailto:linux@panokkel.be), March 2022.
snd-hdspe-1.0.2/dkms.conf 0000664 0000000 0000000 00000000302 15060370513 0015177 0 ustar 00root root 0000000 0000000 PACKAGE_NAME="alsa-hdspe"
PACKAGE_VERSION="0.0"
BUILT_MODULE_LOCATION[0]="./sound/pci/hdsp/hdspe"
BUILT_MODULE_NAME[0]="snd-hdspe"
DEST_MODULE_LOCATION[0]="/updates/dkms/"
AUTOINSTALL="yes"
snd-hdspe-1.0.2/doc/ 0000775 0000000 0000000 00000000000 15060370513 0014144 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/doc/asound.state 0000664 0000000 0000000 00000025477 15060370513 0016516 0 ustar 00root root 0000000 0000000 state.HDSPe24052016 {
control.1 {
iface CARD
name 'Card Revision'
value 212
comment {
access read
type INTEGER
count 1
range '0 - 0 (step 1)'
}
}
control.2 {
iface CARD
name 'Firmware Build'
value 21
comment {
access read
type INTEGER
count 1
range '0 - 0 (step 1)'
}
}
control.3 {
iface CARD
name Serial
value 24052016
comment {
access read
type INTEGER
count 1
range '0 - 0 (step 1)'
}
}
control.4 {
iface CARD
name 'TCO Present'
value true
comment {
access read
type BOOLEAN
count 1
}
}
control.5 {
iface CARD
name 'Capture PID'
value -1
comment {
access 'read volatile'
type INTEGER
count 1
range '0 - 0 (step 1)'
}
}
control.6 {
iface CARD
name 'Playback PID'
value -1
comment {
access 'read volatile'
type INTEGER
count 1
range '0 - 0 (step 1)'
}
}
control.7 {
iface CARD
name Running
value false
comment {
access 'read volatile'
type BOOLEAN
count 1
}
}
control.8 {
iface CARD
name 'Buffer Size'
value 4096
comment {
access 'read volatile'
type INTEGER
count 1
range '64 - 4096 (step 1)'
}
}
control.9 {
iface CARD
name 'Status Polling'
value 10
comment {
access 'read write'
type INTEGER
count 1
range '0 - 1000 (step 1)'
}
}
control.10 {
iface HWDEP
name 'Raw Sample Rate'
value.0 104857600000000
value.1 2377740584
comment {
access 'read volatile'
type INTEGER64
count 2
range '0 - 0'
}
}
control.11 {
iface HWDEP
name DDS
value 2377723356
comment {
access 'read write'
type INTEGER
count 1
range '2026233816 - 3883614814'
}
}
control.12 {
iface CARD
name 'Internal Frequency'
value '44.1 KHz'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '32 KHz'
item.1 '44.1 KHz'
item.2 '48 KHz'
item.3 '64 KHz'
item.4 '88.2 KHz'
item.5 '96 KHz'
item.6 '128 KHz'
item.7 '176.4 KHz'
item.8 '192 KHz'
}
}
control.13 {
iface CARD
name 'Current AutoSync Reference'
value TCO
comment {
access 'read volatile'
type ENUMERATED
count 1
item.0 WordClk
item.1 AES
item.2 S/PDIF
item.3 ADAT
item.4 TCO
item.5 SyncIn
item.6 Intern
}
}
control.14 {
iface CARD
name 'External Frequency'
value '44.1 KHz'
comment {
access 'read volatile'
type ENUMERATED
count 1
item.0 ''
item.1 '32 KHz'
item.2 '44.1 KHz'
item.3 '48 KHz'
item.4 '64 KHz'
item.5 '88.2 KHz'
item.6 '96 KHz'
item.7 '128 KHz'
item.8 '176.4 KHz'
item.9 '192 KHz'
}
}
control.15 {
iface CARD
name 'Clock Mode'
value AutoSync
comment {
access 'read write'
type ENUMERATED
count 1
item.0 AutoSync
item.1 Master
}
}
control.16 {
iface CARD
name 'Preferred AutoSync Reference'
value TCO
comment {
access 'read write'
type ENUMERATED
count 1
item.0 WordClk
item.1 AES
item.2 S/PDIF
item.3 ADAT
item.4 TCO
item.5 SyncIn
}
}
control.17 {
iface CARD
name 'AutoSync Status'
value.0 N/A
value.1 'No Lock'
value.2 'No Lock'
value.3 'No Lock'
value.4 Sync
value.5 'No Lock'
comment {
access 'read volatile'
type ENUMERATED
count 6
item.0 'No Lock'
item.1 Lock
item.2 Sync
item.3 N/A
}
}
control.18 {
iface CARD
name 'AutoSync Frequency'
value.0 ''
value.1 ''
value.2 '48 KHz'
value.3 ''
value.4 '44.1 KHz'
value.5 ''
comment {
access 'read volatile'
type ENUMERATED
count 6
item.0 ''
item.1 '32 KHz'
item.2 '44.1 KHz'
item.3 '48 KHz'
item.4 '64 KHz'
item.5 '88.2 KHz'
item.6 '96 KHz'
item.7 '128 KHz'
item.8 '176.4 KHz'
item.9 '192 KHz'
}
}
control.19 {
iface CARD
name 'S/PDIF In'
value Coaxial
comment {
access 'read write'
type ENUMERATED
count 1
item.0 Optical
item.1 Coaxial
item.2 Internal
}
}
control.20 {
iface CARD
name 'S/PDIF Out Optical'
value false
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.21 {
iface CARD
name 'S/PDIF Out Professional'
value false
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.22 {
iface CARD
name 'ADAT Internal'
value false
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.23 {
iface CARD
name 'Single Speed WordClk Out'
value false
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.24 {
iface CARD
name 'Clear TMS'
value false
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.25 {
iface CARD
name 'Input Level'
value '+13 dBu'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '+4 dBu'
item.1 '+13 dBu'
item.2 '+19 dBu'
item.3 '+24 dBu'
}
}
control.26 {
iface CARD
name 'Output Level'
value '+13 dBu XLR'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '-2 dBu RCA'
item.1 '+4 dBu RCA'
item.2 '+13 dBu RCA'
item.3 '+19 dBu RCA'
item.4 '+4 dBu XLR'
item.5 '+13 dBu XLR'
item.6 '+19 dBu XLR'
item.7 '+24 dBu XLR'
}
}
control.27 {
iface CARD
name 'Phones Level'
value 'Lo Power'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 'Lo Power'
item.1 'Hi Power'
}
}
control.28 {
iface HWDEP
name Mixer
value.0 0
value.1 0
value.2 0
comment {
access 'read write volatile'
type INTEGER
count 3
range '0 - 65535 (step 1)'
}
}
control.29 {
iface MIXER
name Chn
index 1
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.30 {
iface MIXER
name Chn
index 2
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.31 {
iface MIXER
name Chn
index 3
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.32 {
iface MIXER
name Chn
index 4
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.33 {
iface MIXER
name Chn
index 5
value 0
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.34 {
iface MIXER
name Chn
index 6
value 0
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.35 {
iface MIXER
name Chn
index 7
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.36 {
iface MIXER
name Chn
index 8
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.37 {
iface MIXER
name Chn
index 9
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.38 {
iface MIXER
name Chn
index 10
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.39 {
iface MIXER
name Chn
index 11
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.40 {
iface MIXER
name Chn
index 12
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.41 {
iface MIXER
name Chn
index 13
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.42 {
iface MIXER
name Chn
index 14
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.43 {
iface MIXER
name Chn
index 15
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.44 {
iface MIXER
name Chn
index 16
value 64
comment {
access 'read write volatile'
type INTEGER
count 1
range '0 - 64 (step 1)'
}
}
control.45 {
iface CARD
name 'LTC In'
value.0 72060905474621443
value.1 15439372236
comment {
access 'read volatile'
type INTEGER64
count 2
range '0 - 0'
}
}
control.46 {
iface CARD
name 'LTC In Valid'
value true
comment {
access 'read volatile'
type BOOLEAN
count 1
}
}
control.47 {
iface CARD
name 'LTC In Frame Rate'
value '24 fps'
comment {
access 'read volatile'
type ENUMERATED
count 1
item.0 '24 fps'
item.1 '25 fps'
item.2 '29.97 fps'
item.3 '30 fps'
}
}
control.48 {
iface CARD
name 'LTC In Drop Frame'
value false
comment {
access 'read volatile'
type BOOLEAN
count 1
}
}
control.49 {
iface CARD
name 'LTC In Pull Factor'
value 1000
comment {
access 'read volatile'
type INTEGER
count 1
range '0 - 0'
}
}
control.50 {
iface CARD
name 'TCO Video Format'
value 'No video'
comment {
access 'read volatile'
type ENUMERATED
count 1
item.0 'No video'
item.1 NTSC
item.2 PAL
}
}
control.51 {
iface CARD
name 'TCO WordClk Valid'
value false
comment {
access 'read volatile'
type BOOLEAN
count 1
}
}
control.52 {
iface CARD
name 'TCO WordClk Speed'
value 'Single Speed'
comment {
access 'read volatile'
type ENUMERATED
count 1
item.0 'Single Speed'
item.1 'Double Speed'
item.2 'Quad Speed'
}
}
control.53 {
iface CARD
name 'TCO Lock'
value true
comment {
access 'read volatile'
type BOOLEAN
count 1
}
}
control.54 {
iface CARD
name 'LTC Run'
value true
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.55 {
iface CARD
name 'TCO Sample Rate'
value 'from App'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '44.1 KHz'
item.1 '48 KHz'
item.2 'from App'
}
}
control.56 {
iface CARD
name 'TCO Pull'
value '0'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '0'
item.1 '+0.1 %'
item.2 '-0.1 %'
item.3 '+4 %'
item.4 '-4 %'
}
}
control.57 {
iface CARD
name 'TCO WCK Conversion'
value '1:1'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '1:1'
item.1 '44.1 KHz -> 48 KHz'
item.2 '48 KHz -> 44.1 KHz'
}
}
control.58 {
iface CARD
name 'TCO Frame Rate'
value '24 fps'
comment {
access 'read write'
type ENUMERATED
count 1
item.0 '24 fps'
item.1 '25 fps'
item.2 '29.97 fps'
item.3 '29.97 dfps'
item.4 '30 fps'
item.5 '30 dfps'
}
}
control.59 {
iface CARD
name 'TCO Sync Source'
value LTC
comment {
access 'read write'
type ENUMERATED
count 1
item.0 WordClk
item.1 Video
item.2 LTC
}
}
control.60 {
iface CARD
name 'TCO Word Term'
value true
comment {
access 'read write'
type BOOLEAN
count 1
}
}
}
snd-hdspe-1.0.2/doc/controls.md 0000664 0000000 0000000 00000043025 15060370513 0016335 0 ustar 00root root 0000000 0000000 ALSA control elements
=====================
As usual
alsactl -f asound.state store
stores the actual ALSA control elements with all metadata and exact enumeration values
in the file name asound.state.
See [hdspeconf](https://github.com/PhilippeBekaert/hdspeconf) for an example application using (and showing how to use) these elements.
The controls correspond for most part with the similarly named controls in the Windows and MAC OSX control applications provided by RME.
See the RME HDSPe sound card user guides, and [hdspeconf](https://github.com/PhilippeBekaert/hdspeconf) documentation
for more information on these controls.
**Acces modes**:
The access values in the tables below are a combination of the following symbols:
| Symbol | Meaning |
| :- | :- |
| R | Control element is readable |
| W | Control element is writable |
| V | Control element is volatile |
Controls common to all supported cards
--------------------------------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | Card Revision | R | Int | PCI class revision. Uniquely identifies card model. |
| CARD | Firmware Build | R | Int | Firmware version. |
| CARD | Serial | R | Int | Card serial number. |
| CARD | TCO Present | R | Bool | Whether or not TCO module is present. |
| CARD | Capture PID | RV | Int | Current capture process ID, or -1. |
| CARD | Playback PID | RV | Int | Current playback process ID, or -1. |
| CARD | Running | RV | Bool | Whether or not some process is capturing or playing back. |
| CARD | Buffer Size | RV | Int | Sample buffer size, in frames. |
| CARD | Status Polling | RWV | Int | See below **Status Polling** |
| HWDEP | DDS | RW | Int | See below **DDS** |
| HWDEP | Raw Sample Rate | RV | Int64 | See below **DDS** |
| CARD | Clock Mode | RW | Enum | Master or AutoSync. |
| CARD | Preferred AutoSync Reference | RW | Enum | Preferred clock source, if in AutoSync mode. |
| CARD | Current AutoSync Reference | RV | Enum | Current clock source. |
| CARD | AutoSync Status | RV | Enum | AutoSync clock status: N/A, No Lock, Lock or Sync, for all sources. |
| CARD | AutoSync Frequency | RV | Enum | Current clock source sample rate class, for all sources: 32 KHz, 44.1 KHz, 48 KHz, 64 KHz, 88.2 KHz, 96 KHz, 128 KHz 176.4 KHz 192 KHz. Note: MADI cards only report this for the MADI input and not for the other sources. |
| CARD | Internal Frequency | RW | Enum | Internal sampling rate class: 32 KHz, 44.1 KHz, 48 KHz etc.... |
**Status Polling**
Use this control element to enable or disable kernel space status polling. The value of this element is
the frequency at which to perform status polling in the driver, or 0 to disable the feature.
If non-zero, the driver will poll for card changes in the value of volatile control elements
at approximately the indicated frequency.
A notification event is generated on any status ALSA control elements that has changed. If any
have changed, the value of the Status Polling control element is reset to 0, notifying client
applications, and effectively disabling
status polling until a client application enables it again by setting a non-zero value. Status
polling is also automatically disabled after a few seconds. When automatically disabled, a notification
is sent as well, so that client applications can re-enable it. Whenever an application receives
a "Status Polling" event notification, it shall set the value of this control element to the maximum
of its reported value and the applications desired value to avoid ping-ponging changes with other
applications.
**DDS**
The HDSPe cards report effective sampling frequency as a ratio of a fixed frequency constant
typical for the card, and the content of a register. This ratio is returned
in the "Raw Sample Rate" control element. The numerator is the first value, and is a true 64-bit value. The denominator is a 32-bit value, and provided as the second value.
The "DDS" control element enables setting the DDS register, determining internal sample rate to sub-Hz accuracy. The DDS register value is the denominator of the desired sample rate, given as a ratio
with same numerator as the "Raw Sample Rate" control element, i.o.w. the numerator is the first value
of the "Raw Sample Rate" control element.
This can be used to synchronise the cards internal clock to e.g. a system clock.
TCO controls
------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | TCO Firmware | RO | Int | TCO module firmware version |
| CARD | LTC In | RV | Int64 | Incoming LTC code - see below **LTC control** |
| CARD | LTC In Drop Frame | RV | Bool | Whether incoming LTC is drop frame format or not |
| CARD | LTC In Frame Rate | RV | Enum | Incoming **LTC frame rate**: 24, 25 or 30 fps |
| CARD | LTC In Pull Factor | RV | Int | Incoming **LTC frame rate** deviation from standard |
| CARD | LTC In Valid | RV | Bool | Whether or not valid LTC input is detected |
| CARD | LTC Out | W | Int64 | LTC output control - see below **LTC control** |
| CARD | LTC Time | RV | Int64 | Current periods end LTC time - see below **LTC control** |
| CARD | LTC Run | RW | Bool | Pauze / restart LTC output |
| CARD | LTC Frame Rate | RW | Enum | TCO LTC engine frame rate: 24, 25, 29.97, 29.97 DF or 30 fps |
| CARD | LTC Sample Rate | RW | Enum | TCO LTC engine audio sample rate: 44.1 KHz, 48 KHz, **From App** |
| CARD | TCO Lock | RV | Bool | Whether or not the TCO is locked to LTC, Video or Word Clock |
| CARD | TCO Pull | RW | Enum | Pull Up / Pull Down factor |
| CARD | TCO Sync Source | RW | Enum | TCO preferred synchronisation source: LTC, Video or Word Clock |
| CARD | TCO Video Format | RV | Enum | Video format reference signal detected: PAL or NTSC blackburst. Firmware 11 or higher detect SDI reference signals of potential other frame rates, use TCO Video Frame Rate control if TCO firmware >- 11|
| CARD | TCO Video Frame Rate | RV | Enum | Video frame rate detected (meaningful only if TCO firmware >= 11) |
| CARD | TCO WordClk Conversion | RW | Enum | Word clock rate conversion 1:1, 44.1 -> 48 KHz, 48 -> 44.1 KHz |
| CARD | TCO WordClk Term | RW | Bool | Whether or not to 75 Ohm terminate the word clock/video input BNC |
| CARD | TCO WordClk Valid | RV | Bool | Whether or not a valid word clock signal is detected |
| CARD | TCO WordClk Speed | RV | Enum | Detected input word clock speed |
| CARD | TCO WorldClk Out Speed | RW | Enum | Output word clock speed |
**LTC Control**
If a valid LTC signal is presented to the TCO module, the 'LTC In' control
will report two 64-bit values: the current LTC 64-bit code and the LTC time
at which that current code started.
The current LTC code is the code of the last fully received LTC frame, incremented by one frame. The current LTC start time in fact is the time at which that last fully received LTC frame ended. This is correct, and what a user expects, for continuous and forward running LTC. For stationary, jumping, or backward running LTC, the code shall be corrected appropriately.
LTC output is started by writing the 'LTC Out' control.
The 'LTC Out' control contains two 64-bit values similar to the 'LTC In' control
element: the LTC 64-bit code
to start the LTC output with, and the LTC time at which that code shall be
started. If the indicated time is in the past or further in the future
than a few LTC frame durations, the start time and time code are adapted
accordingly to create output that would result if the indicated time code
were started at the indicated time (if in the past) or that will result in
the indicated time code being generated at the indicated time (if in the
future).
The SMPTE 12-1 standard defines LTC as a 80-bit code, with 64 data bits and
16 synchronisation bits. The 64-bit LTC code mentioned above corresponds to
the 64 data bits of an SMPTE 12-1 80-bit code. The data bits are described
in the [SMPTE 12-1 standard](https://ieeexplore.ieee.org/document/7291029/definitions?anchor=definitions) and on [wikipedia](https://en.wikipedia.org/wiki/Linear_timecode).
The LTC time is the number of audio frames processed since the snd-hdspe driver
was started. The 'LTC Time' control allows to read the LTC time corresponding to
the end of the current period. In a jack audio connection kit application
process callback, an application would query the current period jack audio frame
counter using the jack_get_cycle_times() call and add the period size, on
the one hand side. The application would read the LTC time from the 'LTC Time'
control on the other hand size. The thus obtained counters represent the
same moment in time, and allow to convert the time reported in a 'LTC In'
control to a jack audio frame count, or to calculate the
LTC time at which LTC output shall be started from a jack audio frame count.
*Note* ALSA probably provides mechanisms to query clocks, such as the here
described LTC time, more efficiently than through a control element. In
future, the 'LTC Time' control element may be replaced by such more efficient
mechanism.
Examples:
- positional time code is generated by starting code 00:00:00:00 at time 0.
The output time code will reflect the time since the start of the driver.
- jam sync: set 'LTC Out' time code and time to the current 'LTC In' time code
and time.
Outputting wall clock LTC: set 'LTC Out' time code to the special value -1,
and the time to the number of seconds east of GMT corresponding to the
time zone, and corrected for daylight saving time if in effect, if the computers
real time clock is set to UTC and not to local time zone.
The driver will replace a hexadecimal time code of 3f:7f:7f:3f by the exact
value of the real-time clock at the time the request gets processed, and add
the number of seconds indicated in the time field of the control. Such
hexadecimal time code results from setting the first value of the 'LTC Out'
control to -1.
The deviation of local time w.r.t. UTC can be queried with the localtime_r() GNU libc call.
**LTC frame rate**
SMPTE 12-1 time codes contain control bits indicating the frame rate of the LTC: 24, 25 or 30 fps. The frame rate standard of incoming LTC is
reported in the 'LTC In Frame Rate' control.
The effective frame rate may however deviate from what the frame rate bits in the LTC codes indicate. For instance, NTSC 29.97 fps is reported
as 30 fps. The deviation between actual and standard frame rate is reported in the 'LTC In Pull Factor' control. This control returns a value of
1000 for nominal speed, less than 1000 for slower rates and greater than 1000 for higher effective rate. The value results from measuring the
actual LTC frame duration in the driver.
Example: 29.97 NTSC pull down LTC will be reported with a pull factor of 999.
The 'LTC Frame Rate' property controls the TCO LTC engine frame rate. Usually, 'LTC Frame Rate' and 'TCO Pull' shall be set to match the incoming LTC effective frame rate, in order to produce a clean 44.1 KHz or 48 KHz sample clock synchronisation. But it also sets the frame rate for LTC output.
**From App**
The 'From App' LTC sample rate setting will set the TCO LTC engine sample rate
to match the audio card sample rate class: 44.1 KHz if the sound card is
running at 44.1 KHz, and 48 KHz otherwise (the TCO does not support 32 KHz
sample rate).
AES controls:
-------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | Double Speed Mode | RW | Enum | Double speed mode: Single Wire or Double Wire |
| CARD | Quad Speed Mode | RW | Enum | Quad speed mode: Single Wire, Double Wire or Quad Wire |
| CARD | Professional | RW | Bool | If true, output professional mode AES (5V, professional mode status bits). If false, outputs 2V and consumer status bits, compatible with S/PDIF HiFi equipment e.g. |
| CARD | Emphasis | RW | Bool | Enable high frequency emphasis status bit in output. |
| CARD | Non Audio | RW | Bool | Enable non-audio (dolby/AC3) status bits in output. |
| CARD | Line Out | RW | Bool | On by default. Disable for AC3 output. |
| CARD | Single Speed WordClk Out | RW | Bool | Output single-speed word clock signal, also when running in double or quad speed mode |
| CARD | Clear TMS | RW | Bool | Clear track-marker and status bits from AES and ADAT audio samples. If not set, these bits are available as the least significant bits of PCM data. |
AIO controls
------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | Input Level | RW | Enum | Analog audio input reference level: -10 dBV (with 12 dB headroom), +4 dBu (with 9dB headroom), Lo Gain (+4 dBu with 15 dB headroom) |
| CARD | Output Level | RW | Enum | Analog audio output reference level: -10 dBV (with 12 dB headroom), +4 dBu (with 9dB headroom), Hi Gain (+4 dBu with 15 dB headroom) |
| CARD | XLR Breakout Cable | RW | Enum | Analog output breakout cable: XLR or RCA. -6 dB gain correction on XLR for correct reference level |
| CARD | Phones Level | RW | Enum | Headphones output level: same options as Output Level |
| CARD | S/PDIF In | RW | Enum | S/PDIF input connector: coaxial, optical or internal |
| CARD | S/PDIF Out Optical | RW | Bool | Output S/PDIF over TOSLINK |
| CARD | S/PDIF Out Professional | RW | Bool | Output professional mode S/PDIF |
| CARD | ADAT Internal | RW | Bool | Use the internal connector for ADAT, with AEB or TEB expansion board |
| CARD | Single Speed WordClk Out | RW | Bool | Output single-speed word clock signal, also when running in double or quad speed mode |
| CARD | Clear TMS | RW | Bool | Clear track-marker and status bits from AES and ADAT audio samples. If not set, these bits are available as the least significant bits of PCM data. |
| CARD | AO4S Present | RO | Bool | AO4S-192 analog output extension board present |
| CARD | AI4S Present | RO | Bool | AI4S-192 analog input extension board present |
AIO Pro controls
----------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | Input Level | RW | Enum | Analog audio **input level** |
| CARD | Output Level | RW | Enum | Analog audio **output level** |
| CARD | Phones Level | RW | Enum | Headphones output level: High power or Low power |
| CARD | S/PDIF In | RW | Enum | S/PDIF input connector: coaxial, optical or internal |
| CARD | S/PDIF Out Optical | RW | Bool | Output S/PDIF over TOSLINK |
| CARD | S/PDIF Out Professional | RW | Bool | Output professional mode S/PDIF |
| CARD | ADAT Internal | RW | Bool | Use the internal connector for ADAT, with AEB or TEB expansion board |
| CARD | Single Speed WordClk Out | RW | Bool | Output single-speed word clock signal, also when running in double or quad speed mode |
| CARD | Clear TMS | RW | Bool | Clear track-marker and status bits from AES and ADAT audio samples. If not set, these bits are available as the least significant bits of PCM data. |
**Input level**
Full scale PCM input data for analog input coresponds to +4, +13, +19 or +24 dBu level.
**Output level**
Full scale PCM output data for analog output corresponds to +4, +13, +19 or +24 dBu level if outputting balanced audio (using the XLR breakout
cable), or -2, +4, +13 or +19 dBu level if outputting unbalanced audio (using the RCA breakout cable).
MADI controls
-------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | External Frequency | RV | Enum | Frequency class of the current autosync reference: 32KHz, 44.1KHz, 48KHz, etc... (MADI cards do not report the frequency class of each autosync reference individually, like other cards do.) |
| CARD | Preferred Input | RW | Enum | Preferred MADI input: Optical, Coaxial |
| CARD | Autoselect Input | RW | Bool | Whether or not to automatically switch over input if preferred input is not available (a.k.a. safe mode) |
| CARD | Current Input | RV | Enum | Current MADI input: Optical, Coaxial |
| CARD | RX 64 Channels Mode | RV | Bool | Whether or not we're currently receiving 64 channels mode (true) or 56 channels mode (false) MADI input |
| CARD | TX 64 Channels Mode | RW | Bool | Transmit 64 channels mode (true) or 56 channels mode (false) |
| CARD | Double Wire Mode | RW | Bool | Double speed mode: 48K frame mode (= S/MUX or double wire mode) if true, 96K frame (single wire) mode if false |
| CARD | Line Out | RW | Bool | Enable/disable headphone output |
| CARD | Single Speed WordClk Out | RW | Bool | Output single-speed word clock signal, also when running in double or quad speed mode |
| CARD | Clear TMS | RW | Bool | Clear track-marker and status bits from MADI audio samples. If not set, these bits are available as the least significant bits of PCM data. |
RayDAT controls
---------------
| Interface | Name | Access | Value Type | Description |
| :- | :- | :- | :- | :- |
| CARD | S/PDIF In | RW | Enum | S/PDIF input connector: coaxial, optical from ADAT4 connector, or internal |
| CARD | S/PDIF Out Optical | RW | Bool | Output S/PDIF on ADAT4 connector |
| CARD | S/PDIF Out Professional | RW | Bool | Output professional mode S/PDIF |
| CARD | ADAT1 Internal | RW | Bool | Use the internal ADAT1 connector instead of optical, for AEB or TEB expansion board |
| CARD | ADAT2 Internal | RW | Bool | Use the internal ADAT2 connector instead of optical, for AEB or TEB expansion board |
| CARD | Single Speed WordClk Out | RW | Bool | Output single-speed word clock signal, also when running in double or quad speed mode |
| CARD | Clear TMS | RW | Bool | Clear track-marker and status bits from AES and ADAT audio samples. If not set, these bits are available as the least significant bits of PCM data. |
snd-hdspe-1.0.2/sound/ 0000775 0000000 0000000 00000000000 15060370513 0014527 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/sound/pci/ 0000775 0000000 0000000 00000000000 15060370513 0015302 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/sound/pci/hdsp/ 0000775 0000000 0000000 00000000000 15060370513 0016240 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/sound/pci/hdsp/Makefile 0000664 0000000 0000000 00000000106 15060370513 0017675 0 ustar 00root root 0000000 0000000 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_HDSPE) += hdspe/
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/ 0000775 0000000 0000000 00000000000 15060370513 0017343 5 ustar 00root root 0000000 0000000 snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/Makefile 0000664 0000000 0000000 00000000427 15060370513 0021006 0 ustar 00root root 0000000 0000000 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_HDSPE) += snd-hdspe.o
snd-hdspe-objs := hdspe_core.o hdspe_pcm.o hdspe_midi.o hdspe_hwdep.o \
hdspe_proc.o hdspe_control.o hdspe_mixer.o hdspe_tco.o \
hdspe_common.o hdspe_madi.o hdspe_aes.o hdspe_raio.o \
hdspe_ltc_math.o
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe.h 0000664 0000000 0000000 00000071327 15060370513 0020631 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note
/**
* @file hdspe.h
* @brief RME HDSPe driver user space API.
*
* Note: The definitions and structs defined in this header file are used
* within the driver as well as in IOCTLs. As they are used within the
* driver, they are up to date. However, the IOCTLs should be considered
* obsolete: the same information on the driver can be obtained via
* standard ALSA control mechanisms, except for the mixer and level
* meters at this time. To my knowledge (PhB), the only (public) application
* still using the IOCTL interface is hdspmixer. hdspeconf is fully based on
* the ALSA control mechanism. Do not use the IOCTLs for new development -
* inform Philippe.Bekaert@uhasselt.be in case you wouldn't be able to do
* without.
*
* 20210728 ... 0813 - Philippe.Bekaert@uhasselt.be
* 20220329,30 - PhB : API version 3 (TCO related additions)
*
* Based on earlier work by Winfried Ritsch (IEM, 2003) and
* Thomas Charbonnel (thomas@undata.org),
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
#ifndef __SOUND_HDSPE_H
#define __SOUND_HDSPE_H
#include
#include
/* User space API version.
* Structs returned by the HDSPe driver ioctls contain the API version with which the
* kernel driver has been compiled. API users should check that version against
* HDSPE_VERSION and take appropriate action in case versions differ. */
#define HDSPE_VERSION 3
/* Maximum hardware input, software playback and hardware output
* channels is 64 even on 56Mode you have 64playbacks to matrix. */
#define HDSPE_MAX_CHANNELS 64
/* Card model */
enum hdspe_io_type {
HDSPE_MADI = 0,
HDSPE_MADIFACE = 1,
HDSPE_AIO = 2,
HDSPE_AES = 3,
HDSPE_RAYDAT = 4,
HDSPE_AIO_PRO = 5,
HDSPE_IO_TYPE_COUNT = 6,
HDSPE_IO_TYPE_INVALID = 7,
HDSPE_IO_TYPE_FORCE_32BIT = 0xffffffff
};
#define HDSPE_IO_TYPE_NAME(i) \
(i == HDSPE_MADI ? "MADI" : \
i == HDSPE_MADIFACE ? "MADIface" : \
i == HDSPE_AIO ? "AIO" : \
i == HDSPE_AES ? "AES" : \
i == HDSPE_RAYDAT ? "RayDAT" : \
i == HDSPE_AIO_PRO ? "AIO Pro" : \
"???")
/* Clock mode */
enum hdspe_clock_mode {
HDSPE_CLOCK_MODE_AUTOSYNC = 0,
HDSPE_CLOCK_MODE_MASTER = 1,
HDSPE_CLOCK_MODE_COUNT = 2,
HDSPE_CLOCK_MODE_INVALID = 3,
HDSPE_CLOCK_MODE_FORCE_32BIT = 0xffffffff
};
#define HDSPE_CLOCK_MODE_NAME(i) \
(i == HDSPE_CLOCK_MODE_AUTOSYNC ? "AutoSync" : \
i == HDSPE_CLOCK_MODE_MASTER ? "Master" : \
"???")
/* Speed mode */
enum hdspe_speed {
HDSPE_SPEED_SINGLE = 0,
HDSPE_SPEED_DOUBLE = 1,
HDSPE_SPEED_QUAD = 2,
HDSPE_SPEED_COUNT = 3,
HDSPE_SPEED_INVALID = 4,
HDSPE_SPEED_FORCE_32BIT = 0xffffffff
};
#define HDSPE_SPEED_NAME(i) \
(i == HDSPE_SPEED_SINGLE ? "Single Speed" : \
i == HDSPE_SPEED_DOUBLE ? "Double Speed" : \
i == HDSPE_SPEED_QUAD ? "Quad Speed" : \
"???")
/* frequency class */
enum hdspe_freq {
HDSPE_FREQ_NO_LOCK = 0,
HDSPE_FREQ_32KHZ = 1,
HDSPE_FREQ_44_1KHZ = 2,
HDSPE_FREQ_48KHZ = 3,
HDSPE_FREQ_64KHZ = 4,
HDSPE_FREQ_88_2KHZ = 5,
HDSPE_FREQ_96KHZ = 6,
HDSPE_FREQ_128KHZ = 7,
HDSPE_FREQ_176_4KHZ = 8,
HDSPE_FREQ_192KHZ = 9,
HDSPE_FREQ_COUNT = 10,
HDSPE_FREQ_INVALID = 11,
HDSPE_FREQ_FORCE_32BIT= 0xffffffff
};
#define HDSPE_FREQ_NAME(i) \
(i == HDSPE_FREQ_NO_LOCK ? "" : \
i == HDSPE_FREQ_32KHZ ? "32 KHz" : \
i == HDSPE_FREQ_44_1KHZ ? "44.1 KHz" : \
i == HDSPE_FREQ_48KHZ ? "48 KHz" : \
i == HDSPE_FREQ_64KHZ ? "64 KHz" : \
i == HDSPE_FREQ_88_2KHZ ? "88.2 KHz" : \
i == HDSPE_FREQ_96KHZ ? "96 KHz" : \
i == HDSPE_FREQ_128KHZ ? "128 KHz" : \
i == HDSPE_FREQ_176_4KHZ ? "176.4 KHz" : \
i == HDSPE_FREQ_192KHZ ? "192 KHz" : \
"???")
#define HDSPE_FREQ_SAMPLE_RATE(i) \
(i == HDSPE_FREQ_NO_LOCK ? 0 : \
i == HDSPE_FREQ_32KHZ ? 32000 : \
i == HDSPE_FREQ_44_1KHZ ? 44100 : \
i == HDSPE_FREQ_48KHZ ? 48000 : \
i == HDSPE_FREQ_64KHZ ? 64000 : \
i == HDSPE_FREQ_88_2KHZ ? 88200 : \
i == HDSPE_FREQ_96KHZ ? 96000 : \
i == HDSPE_FREQ_128KHZ ? 128000 : \
i == HDSPE_FREQ_176_4KHZ ? 176400 : \
i == HDSPE_FREQ_192KHZ ? 192000 : \
0)
/* Clock source aka AutoSync references */
enum hdspe_clock_source {
HDSPE_CLOCK_SOURCE_WORD = 0, // Word clock
HDSPE_CLOCK_SOURCE_1 = 1, // Digital audio input 1
HDSPE_CLOCK_SOURCE_2 = 2, // Digital audio input 2
HDSPE_CLOCK_SOURCE_3 = 3, // Digital audio input 3
HDSPE_CLOCK_SOURCE_4 = 4, // Digital audio input 4
HDSPE_CLOCK_SOURCE_5 = 5, // Digital audio input 5
HDSPE_CLOCK_SOURCE_6 = 6, // Digital audio input 6
HDSPE_CLOCK_SOURCE_7 = 7, // Digital audio input 7
HDSPE_CLOCK_SOURCE_8 = 8, // Digital audio input 8
HDSPE_CLOCK_SOURCE_TCO = 9, // Time Code Option
HDSPE_CLOCK_SOURCE_SYNC_IN = 10, // Internal Sync input
HDSPE_CLOCK_SOURCE_11 = 11, // Unused
HDSPE_CLOCK_SOURCE_12 = 12, // Unused
HDSPE_CLOCK_SOURCE_13 = 13, // Unused
HDSPE_CLOCK_SOURCE_14 = 14, // Unused
HDSPE_CLOCK_SOURCE_INTERN = 15, // Internal clock ("master mode")
HDSPE_CLOCK_SOURCE_COUNT = 16,
HDSPE_CLOCK_SOURCE_INVALID = 17,
HDSPE_CLOCK_SOURCE_FORCE_32BIT = 0xffffffff
};
/* Synonyms for HDSP_CLOCK_SOURCE_1..8, for each card model: */
#define HDSPE_CLOCK_SOURCE_MADI HDSPE_CLOCK_SOURCE_1 /* MADI */
#define HDSPE_CLOCK_SOURCE_AES1 HDSPE_CLOCK_SOURCE_1 /* AES */
#define HDSPE_CLOCK_SOURCE_AES2 HDSPE_CLOCK_SOURCE_2
#define HDSPE_CLOCK_SOURCE_AES3 HDSPE_CLOCK_SOURCE_3
#define HDSPE_CLOCK_SOURCE_AES4 HDSPE_CLOCK_SOURCE_4
#define HDSPE_CLOCK_SOURCE_AES5 HDSPE_CLOCK_SOURCE_5
#define HDSPE_CLOCK_SOURCE_AES6 HDSPE_CLOCK_SOURCE_6
#define HDSPE_CLOCK_SOURCE_AES7 HDSPE_CLOCK_SOURCE_7
#define HDSPE_CLOCK_SOURCE_AES8 HDSPE_CLOCK_SOURCE_8
#define HDSPE_CLOCK_SOURCE_AES HDSPE_CLOCK_SOURCE_1 /* RayDAT/AIO/AIO Pro */
#define HDSPE_CLOCK_SOURCE_SPDIF HDSPE_CLOCK_SOURCE_2
#define HDSPE_CLOCK_SOURCE_ADAT HDSPE_CLOCK_SOURCE_3 /* AIO/AIO Pro */
#define HDSPE_CLOCK_SOURCE_ADAT1 HDSPE_CLOCK_SOURCE_3 /* RayDAT */
#define HDSPE_CLOCK_SOURCE_ADAT2 HDSPE_CLOCK_SOURCE_4
#define HDSPE_CLOCK_SOURCE_ADAT3 HDSPE_CLOCK_SOURCE_5
#define HDSPE_CLOCK_SOURCE_ADAT4 HDSPE_CLOCK_SOURCE_6
#define HDSPE_MADI_CLOCK_SOURCE_NAME(i) \
(i == HDSPE_CLOCK_SOURCE_WORD ? "WordClk": \
i == HDSPE_CLOCK_SOURCE_MADI ? "MADI" : \
i == HDSPE_CLOCK_SOURCE_TCO ? "TCO" : \
i == HDSPE_CLOCK_SOURCE_SYNC_IN ? "SyncIn" : \
i == HDSPE_CLOCK_SOURCE_INTERN ? "Intern" : \
"???")
#define HDSPE_AES_CLOCK_SOURCE_NAME(i) \
(i == HDSPE_CLOCK_SOURCE_WORD ? "WordClk": \
i == HDSPE_CLOCK_SOURCE_AES1 ? "AES1" : \
i == HDSPE_CLOCK_SOURCE_AES2 ? "AES2" : \
i == HDSPE_CLOCK_SOURCE_AES3 ? "AES3" : \
i == HDSPE_CLOCK_SOURCE_AES4 ? "AES4" : \
i == HDSPE_CLOCK_SOURCE_AES5 ? "AES5" : \
i == HDSPE_CLOCK_SOURCE_AES6 ? "AES6" : \
i == HDSPE_CLOCK_SOURCE_AES7 ? "AES7" : \
i == HDSPE_CLOCK_SOURCE_AES8 ? "AES8" : \
i == HDSPE_CLOCK_SOURCE_TCO ? "TCO" : \
i == HDSPE_CLOCK_SOURCE_SYNC_IN ? "SyncIn" : \
i == HDSPE_CLOCK_SOURCE_INTERN ? "Intern" : \
"???")
#define HDSPE_RAYDAT_CLOCK_SOURCE_NAME(i) \
(i == HDSPE_CLOCK_SOURCE_WORD ? "WordClk": \
i == HDSPE_CLOCK_SOURCE_AES ? "AES" : \
i == HDSPE_CLOCK_SOURCE_SPDIF ? "S/PDIF" : \
i == HDSPE_CLOCK_SOURCE_ADAT1 ? "ADAT1" : \
i == HDSPE_CLOCK_SOURCE_ADAT2 ? "ADAT2" : \
i == HDSPE_CLOCK_SOURCE_ADAT3 ? "ADAT3" : \
i == HDSPE_CLOCK_SOURCE_ADAT4 ? "ADAT4" : \
i == HDSPE_CLOCK_SOURCE_TCO ? "TCO" : \
i == HDSPE_CLOCK_SOURCE_SYNC_IN ? "SyncIn" : \
i == HDSPE_CLOCK_SOURCE_INTERN ? "Intern" : \
"???")
/* AIO & AIO Pro */
#define HDSPE_AIO_CLOCK_SOURCE_NAME(i) \
(i == HDSPE_CLOCK_SOURCE_WORD ? "WordClk": \
i == HDSPE_CLOCK_SOURCE_AES ? "AES" : \
i == HDSPE_CLOCK_SOURCE_SPDIF ? "S/PDIF" : \
i == HDSPE_CLOCK_SOURCE_ADAT ? "ADAT" : \
i == HDSPE_CLOCK_SOURCE_TCO ? "TCO" : \
i == HDSPE_CLOCK_SOURCE_SYNC_IN ? "SyncIn" : \
i == HDSPE_CLOCK_SOURCE_INTERN ? "Intern" : \
"???")
/* (Use this for initializations, or with compile time constants only) */
#define HDSPE_CLOCK_SOURCE_NAME(io_type, i) \
(io_type == HDSPE_MADI ? HDSPE_MADI_CLOCK_SOURCE_NAME(i) : \
io_type == HDSPE_MADIFACE ? HDSPE_MADI_CLOCK_SOURCE_NAME(i) : \
io_type == HDSPE_AES ? HDSPE_AES_CLOCK_SOURCE_NAME(i) : \
io_type == HDSPE_RAYDAT ? HDSPE_RAYDAT_CLOCK_SOURCE_NAME(i) : \
io_type == HDSPE_AIO ? HDSPE_AIO_CLOCK_SOURCE_NAME(i) : \
io_type == HDSPE_AIO_PRO ? HDSPE_AIO_CLOCK_SOURCE_NAME(i) : \
"???")
/* SyncCheck Status: with each AutoSync reference, a lock and sync bit
* are associated. They are converted to a single sync status value: */
enum hdspe_sync_status {
HDSPE_SYNC_STATUS_NO_LOCK = 0,
HDSPE_SYNC_STATUS_LOCK = 1,
HDSPE_SYNC_STATUS_SYNC = 2,
HDSPE_SYNC_STATUS_NOT_AVAILABLE = 3,
HDSPE_SYNC_STATUS_COUNT = 4,
HDSPE_SYNC_STATUS_INVALID = 5,
HDSPE_SYNC_STATUS_FORCE_32BIT = 0xffffffff
};
#define HDSPE_SYNC_STATUS_NAME(i) \
(i == HDSPE_SYNC_STATUS_NO_LOCK ? "No Lock" : \
i == HDSPE_SYNC_STATUS_LOCK ? "Lock" : \
i == HDSPE_SYNC_STATUS_SYNC ? "Sync" : \
i == HDSPE_SYNC_STATUS_NOT_AVAILABLE ? "N/A" : \
"???" )
/* Boolean setting / status */
enum hdspe_bool {
HDSPE_BOOL_OFF = 0,
HDSPE_BOOL_ON = 1,
HDSPE_BOOL_COUNT = 2,
HDSPE_BOOL_INVALID = 3,
HDSPE_BOOL_FORCE_32BIT = 0xffffffff
};
#define HDSPE_BOOL_NAME(i) \
(i == HDSPE_BOOL_OFF ? "Off" : \
i == HDSPE_BOOL_ON ? "On" : \
"???")
/* MADI input source */
enum hdspe_madi_input {
HDSPE_MADI_INPUT_OPTICAL = 0,
HDSPE_MADI_INPUT_COAXIAL = 1,
HDSPE_MADI_INPUT_COUNT = 2,
HDSPE_MADI_INPUT_INVALID = 3,
HDSPE_MADI_INPUT_FORCE_32_BIT = 0xffffffff
};
#define HDSPE_MADI_INPUT_NAME(i) \
(i == HDSPE_MADI_INPUT_OPTICAL ? "Optical" : \
i == HDSPE_MADI_INPUT_COAXIAL ? "Coaxial" : \
"???")
/* Double speed mode. Double wire means that two consecutive
* channels in single speed mode are used for transmitting
* a double speed audio signal (same as S/MUX for MADI). */
enum hdspe_ds_mode {
HDSPE_DS_MODE_SINGLE_WIRE = 0,
HDSPE_DS_MODE_DOUBLE_WIRE = 1,
HDSPE_DS_MODE_COUNT = 2,
HDSPE_DS_MODE_INVALID = 3,
HDSPE_DS_MODE_FORCE_32_BIT = 0xffffffff
};
#define HDSPE_DS_MODE_NAME(i) \
(i == HDSPE_DS_MODE_SINGLE_WIRE ? "Single Wire" : \
i == HDSPE_DS_MODE_DOUBLE_WIRE ? "Double Wire" : \
"???")
/* Quad speed mode. Double wire means that two consecutive channels
* at double speed mode are used to transfor a quad speed audio
* signal. Quad wire means that four channels in single speed mode
* are used to transfer a quad speed audio signal (same as SMUX/4 for MADI). */
enum hdspe_qs_mode {
HDSPE_QS_MODE_SINGLE_WIRE = 0,
HDSPE_QS_MODE_DOUBLE_WIRE = 1,
HDSPE_QS_MODE_QUAD_WIRE = 2,
HDSPE_QS_MODE_COUNT = 3,
HDSPE_QS_MODE_INVALID = 4,
HDSPE_QS_MODE_FORCE_32_BIT = 0xffffffff
};
#define HDSPE_QS_MODE_NAME(i) \
(i == HDSPE_QS_MODE_SINGLE_WIRE ? "Single Wire" : \
i == HDSPE_QS_MODE_DOUBLE_WIRE ? "Double Wire" : \
i == HDSPE_QS_MODE_QUAD_WIRE ? "Quad Wire" : \
"???")
/* RayDAT / AIO / AIO Pro S/PDIF input source */
enum hdspe_raio_spdif_input {
HDSPE_RAIO_SPDIF_INPUT_OPTICAL = 0,
HDSPE_RAIO_SPDIF_INPUT_COAXIAL = 1,
HDSPE_RAIO_SPDIF_INPUT_INTERNAL = 2,
HDSPE_RAIO_SPDIF_INPUT_COUNT = 3,
HDSPE_RAIO_SPDIF_INPUT_INVALID = 4,
HDSPE_RAIO_SPDIF_INPUT_FORCE_32BIT = 0xffffffff
};
#define HDSPE_RAIO_SPDIF_INPUT_NAME(i) \
(i == HDSPE_RAIO_SPDIF_INPUT_OPTICAL ? "Optical" : \
i == HDSPE_RAIO_SPDIF_INPUT_COAXIAL ? "Coaxial" : \
i == HDSPE_RAIO_SPDIF_INPUT_INTERNAL ? "Internal" : \
"???")
/* AIO Input / Output / Phones levels */
enum hdspe_aio_level {
HDSPE_AIO_LEVEL_HI_GAIN = 0,
HDSPE_AIO_LEVEL_PLUS_4_DBU = 1,
HDSPE_AIO_LEVEL_MINUS_10_DBV = 2,
HDSPE_AIO_LEVEL_COUNT = 3,
HDSPE_AIO_LEVEL_INVALID = 4,
HDSPE_AIO_LEVEL_FORCE_32BIT = 0xffffffff
};
#define HDSPE_AIO_LEVEL_NAME(i) \
(i == HDSPE_AIO_LEVEL_HI_GAIN ? "Hi Gain" : \
i == HDSPE_AIO_LEVEL_PLUS_4_DBU ? "+4 dBu" : \
i == HDSPE_AIO_LEVEL_MINUS_10_DBV ? "-10 dBV" : \
"???")
/* AIO Pro Input levels. Define sensitivity of the input circuitery:
* the voltage that causes 0 dBFS digital input. */
enum hdspe_aio_pro_input_level {
HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_4_DBU = 0,
HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_13_DBU = 1,
HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_19_DBU = 2,
HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_24_DBU = 3,
HDSPE_AIO_PRO_INPUT_LEVEL_COUNT = 4,
HDSPE_AIO_PRO_INPUT_LEVEL_INVALID = 5,
HDSPE_AIO_PRO_INPUT_LEVEL_FORCE_32BIT = 0xffffffff
};
#define HDSPE_AIO_PRO_INPUT_LEVEL_NAME(i) \
(i == HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_4_DBU ? "+4 dBu" : \
i == HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_13_DBU ? "+13 dBu" : \
i == HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_19_DBU ? "+19 dBu" : \
i == HDSPE_AIO_PRO_INPUT_LEVEL_PLUS_24_DBU ? "+24 dBu" : \
"???")
/* AIO Pro Output levels. Defines the output voltage caused by
* 0 dBFS digital output. */
enum hdspe_aio_pro_output_level {
HDSPE_AIO_PRO_OUTPUT_LEVEL_MINUS_2_DBU_RCA = 0,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_4_DBU_RCA = 1,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_13_DBU_RCA = 2,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_19_DBU_RCA = 3,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_4_DBU_XLR = 4,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_13_DBU_XLR = 5,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_19_DBU_XLR = 6,
HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_24_DBU_XLR = 7,
HDSPE_AIO_PRO_OUTPUT_LEVEL_COUNT = 8,
HDSPE_AIO_PRO_OUTPUT_LEVEL_INVALID = 9,
HDSPE_AIO_PRO_OUTPUT_LEVEL_FORCE_32BIT = 0xffffffff
};
#define HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(i) \
(i == HDSPE_AIO_PRO_OUTPUT_LEVEL_MINUS_2_DBU_RCA ? "-2 dBu RCA" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_4_DBU_RCA ? "+4 dBu RCA" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_13_DBU_RCA ? "+13 dBu RCA" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_19_DBU_RCA ? "+19 dBu RCA" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_4_DBU_XLR ? "+4 dBu XLR" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_13_DBU_XLR ? "+13 dBu XLR" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_19_DBU_XLR ? "+19 dBu XLR" : \
i == HDSPE_AIO_PRO_OUTPUT_LEVEL_PLUS_24_DBU_XLR ? "+24 dBu XLR" : \
"???")
/* AIO Pro Phones levels */
enum hdspe_aio_pro_phones_level {
HDSPE_AIO_PRO_PHONES_LEVEL_LO_POWER = 0,
HDSPE_AIO_PRO_PHONES_LEVEL_HI_POWER = 1,
HDSPE_AIO_PRO_PHONES_LEVEL_COUNT = 2,
HDSPE_AIO_PRO_PHONES_LEVEL_INVALID = 3,
HDSPE_AIO_PRO_PHONES_LEVEL_FORCE_32BIT = 0xffffffff
};
#define HDSPE_AIO_PRO_PHONES_LEVEL_NAME(i) \
(i == HDSPE_AIO_PRO_PHONES_LEVEL_LO_POWER ? "Lo Power" : \
i == HDSPE_AIO_PRO_PHONES_LEVEL_HI_POWER ? "Hi Power" : \
"???")
/* -------------------- IOCTL Peak/RMS Meters -------------------- */
struct hdspe_peak_rms {
uint32_t input_peaks[64];
uint32_t playback_peaks[64];
uint32_t output_peaks[64];
uint64_t input_rms[64];
uint64_t playback_rms[64];
uint64_t output_rms[64];
uint8_t speed; /* enum {ss, ds, qs} */
int32_t status2;
};
#define SNDRV_HDSPE_IOCTL_GET_PEAK_RMS \
_IOR('H', 0x42, struct hdspe_peak_rms)
/* ------------ CONFIG block IOCTL ---------------------- */
struct hdspe_config {
unsigned char pref_sync_ref;
unsigned char wordclock_sync_check;
unsigned char madi_sync_check;
unsigned int system_sample_rate;
unsigned int autosync_sample_rate;
unsigned char system_clock_mode;
unsigned char clock_source;
unsigned char autosync_ref;
unsigned char line_out;
unsigned int passthru;
unsigned int analog_out;
};
#define SNDRV_HDSPE_IOCTL_GET_CONFIG \
_IOR('H', 0x41, struct hdspe_config)
/* ------------ Time Code Option (TCO) IOCTL --------------- */
enum hdspe_ltc_frame_rate {
HDSPE_LTC_FRAME_RATE_24 =0,
HDSPE_LTC_FRAME_RATE_25 =1,
HDSPE_LTC_FRAME_RATE_29_97 =2,
HDSPE_LTC_FRAME_RATE_30 =3,
HDSPE_LTC_FRAME_RATE_COUNT =4,
HDSPE_LTC_FRAME_RATE_FORCE_32BIT =0xffffffff
};
#define HDSPE_LTC_FRAME_RATE_NAME(i) \
(i == HDSPE_LTC_FRAME_RATE_24 ? "24" : \
i == HDSPE_LTC_FRAME_RATE_25 ? "25" : \
i == HDSPE_LTC_FRAME_RATE_29_97 ? "29.97" : \
i == HDSPE_LTC_FRAME_RATE_30 ? "30" : \
"???")
enum hdspe_video_format {
HDSPE_VIDEO_FORMAT_NO_VIDEO =0,
HDSPE_VIDEO_FORMAT_NTSC =1,
HDSPE_VIDEO_FORMAT_PAL =2,
HDSPE_VIDEO_FORMAT_COUNT =3,
HDSPE_VIDEO_FORMAT_FORCE_32BIT =0xffffffff
};
#define HDSPE_VIDEO_FORMAT_NAME(i) \
(i == HDSPE_VIDEO_FORMAT_NO_VIDEO ? "" : \
i == HDSPE_VIDEO_FORMAT_NTSC ? "NTSC" : \
i == HDSPE_VIDEO_FORMAT_PAL ? "PAL" : \
"???")
enum hdspe_video_fps { /* TCO firmware version 11 and above */
HDSPE_VIDEO_FPS_NO_VIDEO =0,
HDSPE_VIDEO_FPS_23_98 =1,
HDSPE_VIDEO_FPS_24 =2,
HDSPE_VIDEO_FPS_25 =3,
HDSPE_VIDEO_FPS_29_97 =4,
HDSPE_VIDEO_FPS_30 =5,
HDSPE_VIDEO_FPS_47_95 =6,
HDSPE_VIDEO_FPS_48 =7,
HDSPE_VIDEO_FPS_50 =8,
HDSPE_VIDEO_FPS_59_94 =9,
HDSPE_VIDEO_FPS_60 =10,
HDSPE_VIDEO_FPS_COUNT =11,
HDSPE_VIDEO_FOS_FORCE_32BIT = 0xffffffff
};
#define HDSPE_VIDEO_FPS_NAME(i) \
(i == HDSPE_VIDEO_FPS_NO_VIDEO ? "" : \
i == HDSPE_VIDEO_FPS_23_98 ? "23.98" : \
i == HDSPE_VIDEO_FPS_24 ? "24" : \
i == HDSPE_VIDEO_FPS_25 ? "25" : \
i == HDSPE_VIDEO_FPS_29_97 ? "29.97" : \
i == HDSPE_VIDEO_FPS_30 ? "30" : \
i == HDSPE_VIDEO_FPS_47_95 ? "47.95" : \
i == HDSPE_VIDEO_FPS_48 ? "48" : \
i == HDSPE_VIDEO_FPS_50 ? "50" : \
i == HDSPE_VIDEO_FPS_59_94 ? "59.94" : \
i == HDSPE_VIDEO_FPS_60 ? "60" : \
"???")
enum hdspe_tco_source {
HDSPE_TCO_SOURCE_WCK =0,
HDSPE_TCO_SOURCE_VIDEO =1,
HDSPE_TCO_SOURCE_LTC =2,
HDSPE_TCO_SOURCE_COUNT =3,
HDSPE_TCO_SOURCE_FORCE_32BIT =0xffffffff
};
#define HDSPE_TCO_SOURCE_NAME(i) \
(i == HDSPE_TCO_SOURCE_WCK ? "Word Clk" : \
i == HDSPE_TCO_SOURCE_VIDEO ? "Video" : \
i == HDSPE_TCO_SOURCE_LTC ? "LTC" : \
"???")
enum hdspe_pull {
HDSPE_PULL_NONE =0,
HDSPE_PULL_UP_0_1 =1,
HDSPE_PULL_DOWN_0_1 =2,
HDSPE_PULL_UP_4 =3,
HDSPE_PULL_DOWN_4 =4,
HDSPE_PULL_COUNT =5,
HDSPE_PULL_FORCE_32BIT =0xffffffff
};
#define HDSPE_PULL_NAME(i) \
(i == HDSPE_PULL_NONE ? "0" : \
i == HDSPE_PULL_UP_0_1 ? "+0.1 %" : \
i == HDSPE_PULL_DOWN_0_1 ? "-0.1 %" : \
i == HDSPE_PULL_UP_4 ? "+4 %" : \
i == HDSPE_PULL_DOWN_4 ? "-4 %" : \
"???")
enum hdspe_tco_sample_rate {
HDSPE_TCO_SAMPLE_RATE_44_1 =0,
HDSPE_TCO_SAMPLE_RATE_48 =1,
HDSPE_TCO_SAMPLE_RATE_FROM_APP =2,
HDSPE_TCO_SAMPLE_RATE_COUNT =3,
HDSPE_TCO_SAMPLE_RATE_FORCE_32BIT = 0xffffffff
};
#define HDSPE_TCO_SAMPLE_RATE_NAME(i) \
(i == HDSPE_TCO_SAMPLE_RATE_44_1 ? "44.1 KHz" : \
i == HDSPE_TCO_SAMPLE_RATE_48 ? "48 KHz" : \
i == HDSPE_TCO_SAMPLE_RATE_FROM_APP ? "From App" : \
"???")
enum hdspe_wck_conversion {
HDSPE_WCK_CONVERSION_1_1 =0,
HDSPE_WCK_CONVERSION_44_1_48 =1,
HDSPE_WCK_CONVERSION_48_44_1 =2,
HDSPE_WCK_CONVERSION_COUNT =3,
HDSPE_WCK_CONVERSION_FORCE_32BIT =0xffffffff
};
#define HDSPE_WCK_CONVERSION_NAME(i) \
(i == HDSPE_WCK_CONVERSION_1_1 ? "1:1" : \
i == HDSPE_WCK_CONVERSION_44_1_48 ? "44.1 KHz -> 48 KHz" : \
i == HDSPE_WCK_CONVERSION_48_44_1 ? "48 KHz -> 44.1 KHz" : \
"???")
#ifdef NEVER
#pragma scalar_storage_order little-endian
/* Raw 32-bit HDSPe LTC code - does not include user bits (TCO doesn't
* report user bits) */
struct hdspe_raw_ltc { uint32_t
frame_units : 4,
frame_tens : 2,
drop_frame : 1,
color_frame : 1,
sec_units : 4,
sec_tens : 3,
flag1 : 1,
min_units : 4,
min_tens : 3,
flag2 : 1,
hour_units : 4,
hour_tens : 2,
clock_flag : 1,
flag3 : 1;
};
/* Generic 64-bit LTC code including user bits (all 0 for HDSPe TCO),
* as returned by the "TCO LTC In" ALSA control. */
struct hdspe_ltc { uint64_t
frame_units : 4,
user1 : 4,
frame_tens : 2,
drop_frame : 1,
color_frame : 1,
user2 : 4,
sec_units : 4,
user3 : 4,
sec_tens : 3,
flag1 : 1,
user4 : 4,
min_units : 4,
user5 : 4,
min_tens : 3,
flag2 : 1,
user6 : 4,
hour_units : 4,
user7 : 4,
hour_tens : 2,
clock_flag : 1,
flag3 : 1,
user8 : 4;
};
#pragma scalar_storage_order default
#endif /*NEVER*/
struct hdspe_tco_status {
uint32_t version;
uint32_t ltc_in;
uint32_t ltc_in_offset;
enum hdspe_bool tco_lock;
enum hdspe_bool ltc_valid;
enum hdspe_ltc_frame_rate ltc_in_fps;
enum hdspe_bool ltc_in_drop;
enum hdspe_video_format video;
enum hdspe_bool wck_valid;
enum hdspe_speed wck_speed;
enum hdspe_tco_source input;
enum hdspe_ltc_frame_rate ltc_fps;
enum hdspe_bool ltc_drop;
enum hdspe_tco_sample_rate sample_rate;
enum hdspe_pull pull;
enum hdspe_wck_conversion wck_conversion;
enum hdspe_bool term;
// LTC output control
enum hdspe_bool ltc_run;
enum hdspe_bool ltc_flywheel;
// HDSPE_VERSION 3:
uint32_t fw_version;
uint32_t fs_period_counter;
enum hdspe_video_fps video_in_fps; // if fw_version >= 11
enum hdspe_speed wck_out_speed;
};
#define SNDRV_HDSPE_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspe_tco_status)
/* ------------ STATUS block IOCTL ---------------------- */
/*
* The status data reflects the device's current state
* as determined by the card's configuration and
* connection status.
*/
/* Device status */
struct hdspe_status {
// API version (this structs size and layout may change with version)
uint32_t version;
// Exact system sample rate as a fraction of a 64-bit unsigned
// integer over a 32-bit unsigned integer. The numerator is
// the cards frequency constant adapted to the current speed
// mode. The denominator is the content of the DDS hardware
// status register.
uint32_t sample_rate_denominator;
uint64_t sample_rate_numerator;
// Exact internal sample rate is sample_rate_numerator /
// internal_sample_rate_denominator. internal_sample_rate_denominator
// is the content of the DDS hardware control register.
uint32_t internal_sample_rate_denominator;
// Period buffer size in number of samples.
uint32_t buffer_size;
// Whether the card is running or not, and what processes
// are capturing or playing back on the card (-1 if none).
// Frequency shall not be changed by means of the snd_ctls
// if running.
enum hdspe_bool running;
pid_t capture_pid;
pid_t playback_pid;
// Device clock mode: AutoSync (0) or Master (1).
enum hdspe_clock_mode clock_mode;
// Frequency class of the internal clock. The internal clock
// is used when in Master clock mode, or no valid AutoSync
// reference is detected.
enum hdspe_freq internal_freq;
// Preferred clock source for AutoSync clock mode.
enum hdspe_clock_source preferred_ref;
// Active clock source ("AutoSync reference").
enum hdspe_clock_source autosync_ref;
// AutoSync clock source sync status.
enum hdspe_sync_status sync[HDSPE_CLOCK_SOURCE_COUNT];
// AutoSync clock source frequency class.
enum hdspe_freq freq[HDSPE_CLOCK_SOURCE_COUNT];
// Frequency class of the active AutoSync reference, if any. Used
// if in AutoSync clock mode and a valid AutoSync reference is
// available (autosync_ref != HDSPE_SYNC_INTERN).
enum hdspe_freq external_freq;
// Single-speed word clock output. If false, word clock output
// is at the systems sample rate. If true, word clock output
// frequency is the systems sample rate reduced to single speed.
enum hdspe_bool wck48;
// Current speed mode.
enum hdspe_speed speed_mode;
// Clear track markers. Affects channel status and track marker
// bits in AES, S/PDIF, ADAT and MADI input.
enum hdspe_bool clr_tms;
// union hdspe_card_specific_status {
// MADI specific status
struct hdspe_status_madi {
enum hdspe_madi_input input_select;
enum hdspe_bool auto_select;
enum hdspe_bool tx_64ch;
enum hdspe_bool smux;
enum hdspe_madi_input input_source;
enum hdspe_bool rx_64ch;
} madi;
// AES specific status
struct hdspe_status_aes {
enum hdspe_bool pro;
enum hdspe_bool emp;
enum hdspe_bool dolby;
enum hdspe_bool lineout;
enum hdspe_ds_mode ds_mode;
enum hdspe_qs_mode qs_mode;
uint32_t aes_mode;
} aes;
// RayDAT / AIO / AIO Pro specific status
struct hdspe_status_raio {
// AO4S Output Audio Extension Board presence (AIO only)
enum hdspe_bool aebo;
// AI4S Input Audio Extension Board presence (AIO only)
enum hdspe_bool aebi;
// ADAT internal (for old AEB / TEB expansion boards)
enum hdspe_bool aeb1, aeb2; // aeb2 is RayDAT only
// S/PDIF input source.
enum hdspe_raio_spdif_input spdif_in;
// S/PDIF optical output.
enum hdspe_bool spdif_opt;
// S/PDIF professional format.
enum hdspe_bool spdif_pro;
union {
// AIO analog I/O levels
struct {
enum hdspe_aio_level input_level;
enum hdspe_aio_level output_level;
enum hdspe_aio_level phones_level;
enum hdspe_bool xlr;
} aio;
// AIO Pro analog I/O levels
struct {
enum hdspe_aio_pro_input_level
input_level;
enum hdspe_aio_pro_output_level
output_level;
enum hdspe_aio_pro_phones_level
phones_level;
uint32_t reserved;
} aio_pro;
};
} raio;
// };
};
#define SNDRV_HDSPE_IOCTL_GET_STATUS \
_IOR('H', 0x49, struct hdspe_status)
/* 47 is hdspm status - incompatible */
/* ------------- Card information --------------- */
/*
* DEPRECATED (hdspm compatible)
*/
#define HDSPE_ADDON_TCO 1
struct hdspe_version {
__u8 card_type; /* enum hdspe_io_type */
char cardname[20];
unsigned int serial;
unsigned short firmware_rev;
int addons;
};
#define SNDRV_HDSPE_IOCTL_GET_VERSION _IOR('H', 0x48, struct hdspe_version)
/*
* Use the following in new developments.
*/
#define HDSPE_EXPANSION_TCO 0x01
#define HDSPE_EXPANSION_AI4S 0x02
#define HDSPE_EXPANSION_AO4S 0x04
#ifndef PCI_VENDOR_ID_XILINX
#define PCI_VENDOR_ID_XILINX 0x10ee
#endif
#ifndef PCI_VENDOR_ID_RME
#define PCI_VENDOR_ID_RME 0x1d18
#endif
struct hdspe_card_info {
// API version (this structs size and layout may change with version)
uint32_t version;
enum hdspe_io_type card_type;
uint32_t serial; /* serial nr */
uint32_t fw_rev; // firmware revision
uint32_t fw_build; // firmware build
uint32_t irq;
uint64_t port;
uint32_t vendor_id; // PCI vendor ID: Xilinx or RME
uint32_t expansion;
};
#define SNDRV_HDSPE_IOCTL_GET_CARD_INFO \
_IOR('H', 0x45, struct hdspe_status)
/* ------------- Matrix Mixer IOCTL --------------- */
/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte =
* 32768 Bytes
*/
/* Organisation is 64 channelfader in a continuous memory block,
* equivalent to hardware definition, maybe for future feature of mmap of
* them.
* Each of 64 outputs has 64 input faders and 64 software playback faders.
* Input in to output out fader is mixer.ch[out].in[in].
* Software playback pb to output out fader is mixer.ch[out].pb[pb] */
/* Mixer Values */
#define HDSPE_UNITY_GAIN 32768 /* = 65536/2 */
#define HDSPE_MINUS_INFINITY_GAIN 0
#define HDSPE_MIXER_CHANNELS HDSPE_MAX_CHANNELS
struct hdspe_channelfader {
uint32_t in[HDSPE_MIXER_CHANNELS];
uint32_t pb[HDSPE_MIXER_CHANNELS];
};
struct hdspe_mixer {
struct hdspe_channelfader ch[HDSPE_MIXER_CHANNELS];
};
struct hdspe_mixer_ioctl {
struct hdspe_mixer *mixer;
};
/* use indirect access due to the limit of ioctl bit size */
#define SNDRV_HDSPE_IOCTL_GET_MIXER _IOR('H', 0x44, struct hdspe_mixer_ioctl)
/* typedefs for compatibility to user-space */
typedef struct hdspe_peak_rms hdspe_peak_rms_t;
typedef struct hdspe_config_info hdspe_config_info_t;
typedef struct hdspe_version hdspe_version_t;
typedef struct hdspe_channelfader snd_hdspe_channelfader_t;
typedef struct hdspe_mixer hdspe_mixer_t;
#endif /* __SOUND_HDSPE_H */
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_aes.c 0000664 0000000 0000000 00000042254 15060370513 0021451 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* hdspe_aes.c
* @brief RME HDSPe AES driver methods.
*
* 20210728,1125 - Philippe.Bekaert@uhasselt.be
*
* Based on earlier work of the other MODULE_AUTHORs,
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
// TODO:
// - Double Wire / Quad Wire mode: fewer channels.
// - FormatAC3
// - status2 AES mode ???
// - WCLK termination
#include "hdspe.h"
#include "hdspe_core.h"
/* Map AES WR_CONTROL / RD_STATUS0 sync ref 4-bit code to hdspe_clock_source
* enum: identity mapping except for the unused codes. */
static enum hdspe_clock_source aes_autosync_ref[16] = {
HDSPE_CLOCK_SOURCE_WORD,
HDSPE_CLOCK_SOURCE_AES1,
HDSPE_CLOCK_SOURCE_AES2,
HDSPE_CLOCK_SOURCE_AES3,
HDSPE_CLOCK_SOURCE_AES4,
HDSPE_CLOCK_SOURCE_AES5,
HDSPE_CLOCK_SOURCE_AES6,
HDSPE_CLOCK_SOURCE_AES7,
HDSPE_CLOCK_SOURCE_AES8,
HDSPE_CLOCK_SOURCE_TCO,
HDSPE_CLOCK_SOURCE_SYNC_IN,
HDSPE_CLOCK_SOURCE_INTERN, // unused codes
HDSPE_CLOCK_SOURCE_INTERN,
HDSPE_CLOCK_SOURCE_INTERN,
HDSPE_CLOCK_SOURCE_INTERN,
HDSPE_CLOCK_SOURCE_INTERN // master clock mode
};
const char* const hdspe_aes_clock_source_names[HDSPE_CLOCK_SOURCE_COUNT] = {
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 0),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 1),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 2),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 3),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 4),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 5),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 6),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 7),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 8),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 9),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 10),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 11),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 12),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 13),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 14),
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, 15),
};
/* Number of channels for different Speed Modes */
#define AES_CHANNELS 16
/* port names */
static const char * const texts_ports_aes[] = {
"AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
"AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
"AES.15", "AES.16"
};
/* These tables map the ALSA channels 1..N to the channels that we
need to use in order to find the relevant channel buffer. RME
refers to this kind of mapping as between "the ADAT channel and
the DMA channel." We index it using the logical audio channel,
and the value is the DMA channel (i.e. channel buffer number)
where the data for that channel can be read/written from/to.
*/
static const char channel_map_aes[HDSPE_MAX_CHANNELS] = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
#ifdef CONFIG_SND_DEBUG
/* WR_CONTROL register bit names for AES card */
static const char* const aes_control_bitNames[32] = {
"START",
"LAT_0",
"LAT_1",
"LAT_2",
"Master",
"IE_AUDIO",
"freq0",
"freq1",
"freq2",
"PRO",
"EMP",
"Dolby",
"?12",
"SyncRef2",
"?14",
"?15",
"SyncRef0",
"SyncRef1",
"SMUX",
"CLR_TMS",
"WCK48",
"IEN2",
"IEN0",
"IEN1",
"LineOut",
"SyncRef3",
"DS_DoubleWire",
"QS_DoubleWire",
"QS_QuadWire",
"?29",
"AES_float_format",
"freq3"
};
/* RD_STATUS2 register bit names for AES */
static const char* const aes_status2_bitNames[32] = {
"lock_aes8",
"lock_aes7",
"lock_aes6",
"lock_aes5",
"lock_aes4",
"lock_aes3",
"lock_aes2",
"lock_aes1",
"sync_aes8",
"sync_aes7",
"sync_aes6",
"sync_aes5",
"sync_aes4",
"sync_aes3",
"sync_aes2",
"sync_aes1",
"aes_mode0",
"aes_mode1",
"aes_mode2",
"aes_mode3",
"sync_in_lock",
"sync_in_sync",
"sync_in_freq0",
"sync_in_freq1",
"sync_in_freq2",
"sync_in_freq3",
"?26",
"?27",
"?28",
"?29",
"?30",
"?31"
};
#endif /*CONFIG_SND_DEBUG*/
static void hdspe_aes_read_status(struct hdspe* hdspe,
struct hdspe_status* status)
{
int i;
struct hdspe_control_reg_aes control = hdspe->reg.control.aes;
struct hdspe_status0_reg_aes status0 = hdspe_read_status0(hdspe).aes;
struct hdspe_status2_reg_aes status2 = hdspe_read_status2(hdspe).aes;
u32 fbits = hdspe_read_fbits(hdspe);
status->version = HDSPE_VERSION;
hdspe_read_sample_rate_status(hdspe, status);
status->clock_mode = control.Master;
status->internal_freq = hdspe_internal_freq(hdspe);
status->speed_mode = hdspe_speed_mode(hdspe);
status->preferred_ref =
(control.SyncRef3 << 3) |
(control.SyncRef2 << 2) |
(control.SyncRef1 << 1) |
(control.SyncRef0 << 0);
status->autosync_ref = status0.sync_ref;
hdspe_set_sync_source(status, HDSPE_CLOCK_SOURCE_WORD,
status0.wc_freq,
status0.wc_lock,
status0.wc_sync, true);
for (i = 0; i < 8; i ++) {
hdspe_set_sync_source(status, HDSPE_CLOCK_SOURCE_AES1+i,
HDSPE_FBITS_FREQ(fbits, i),
status2.lock & (0x80 >> i),
status2.sync & (0x80 >> i), true);
}
hdspe_set_sync_source(status, HDSPE_CLOCK_SOURCE_TCO,
status0.tco_freq,
status0.tco_lock,
status0.tco_sync,
status0.tco_detect);
hdspe_set_sync_source(status, HDSPE_CLOCK_SOURCE_SYNC_IN,
status2.sync_in_freq,
status2.sync_in_lock,
status2.sync_in_sync, true);
for (i = HDSPE_CLOCK_SOURCE_SYNC_IN+1; i < HDSPE_CLOCK_SOURCE_COUNT; i++) {
hdspe_set_sync_source(status, i,
0, false, false, false);
}
status->external_freq = hdspe_speed_adapt(
status->freq[status->autosync_ref],
status->speed_mode);
status->wck48 = control.WCK48;
status->clr_tms = control.CLR_TMS;
// AES specific settings
status->aes.pro = control.PRO;
status->aes.emp = control.EMP;
status->aes.dolby = control.Dolby;
status->aes.lineout = control.LineOut;
status->aes.ds_mode = control.ds_mode;
status->aes.qs_mode = control.qs_mode;
// AES specific status
status->aes.aes_mode = status2.aes_mode;
}
static void hdspe_aes_set_float_format(struct hdspe* hdspe, bool val)
{
hdspe->reg.control.aes.FloatFmt = val;
hdspe_write_control(hdspe);
}
static bool hdspe_aes_get_float_format(struct hdspe* hdspe)
{
return hdspe->reg.control.aes.FloatFmt;
}
static enum hdspe_clock_mode hdspe_aes_get_clock_mode(struct hdspe *hdspe)
{
return hdspe->reg.control.aes.Master;
}
static void hdspe_aes_set_clock_mode(struct hdspe* hdspe,
enum hdspe_clock_mode master)
{
hdspe->reg.control.aes.Master = master;
hdspe_write_control(hdspe);
}
static enum hdspe_clock_source hdspe_aes_get_preferred_sync_ref(
struct hdspe* hdspe)
{
struct hdspe_control_reg_aes control = hdspe->reg.control.aes;
return aes_autosync_ref[(control.SyncRef3 << 3) |
(control.SyncRef2 << 2) |
(control.SyncRef1 << 1) |
(control.SyncRef0 << 0)];
}
static void hdspe_aes_set_preferred_sync_ref(struct hdspe* hdspe,
enum hdspe_clock_source ref)
{
struct hdspe_control_reg_aes* control = &hdspe->reg.control.aes;
if (aes_autosync_ref[ref] == HDSPE_CLOCK_SOURCE_INTERN) ref = 0;
control->SyncRef3 = (ref>>3)&1;
control->SyncRef2 = (ref>>2)&1;
control->SyncRef1 = (ref>>1)&1;
control->SyncRef0 = (ref>>0)&1;
hdspe_write_control(hdspe);
}
static enum hdspe_clock_source hdspe_aes_get_autosync_ref(struct hdspe* hdspe)
{
return aes_autosync_ref[hdspe_read_status0(hdspe).aes.sync_ref];
}
static void hdspe_aes_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
struct hdspe *hdspe = entry->private_data;
struct hdspe_status s;
hdspe_proc_read_common(buffer, hdspe, &s);
snd_iprintf(buffer, "Professional\t\t: %d %s\n", s.aes.pro,
HDSPE_BOOL_NAME(s.aes.pro));
snd_iprintf(buffer, "Emphasis\t\t: %d %s\n", s.aes.emp,
HDSPE_BOOL_NAME(s.aes.emp));
snd_iprintf(buffer, "Non-audio\t\t: %d %s\n", s.aes.dolby,
HDSPE_BOOL_NAME(s.aes.dolby));
snd_iprintf(buffer, "Line Out\t\t: %d %s\n", s.aes.lineout,
HDSPE_BOOL_NAME(s.aes.lineout));
snd_iprintf(buffer, "Double speed mode\t: %d %s\n", s.aes.ds_mode,
HDSPE_DS_MODE_NAME(s.aes.ds_mode));
snd_iprintf(buffer, "Quad speed mode\t\t: %d %s\n", s.aes.qs_mode,
HDSPE_QS_MODE_NAME(s.aes.qs_mode));
snd_iprintf(buffer, "AES mode\t\t: %d %01x\n", s.aes.aes_mode,
s.aes.aes_mode);
snd_iprintf(buffer, "\n");
IPRINTREG(buffer, "CONTROL", hdspe->reg.control.raw,
aes_control_bitNames);
{
union hdspe_status2_reg status2 = hdspe_read_status2(hdspe);
u32 fbits = hdspe_read_fbits(hdspe);
IPRINTREG(buffer, "STATUS2", status2.raw,
aes_status2_bitNames);
hdspe_iprint_fbits(buffer, "FBITS", fbits);
}
hdspe_proc_read_common2(buffer, hdspe, &s);
#ifdef OLDSTUFF
struct hdspe *hdspe = entry->private_data;
unsigned int status;
unsigned int status2;
unsigned int timecode;
unsigned int wcLock, wcSync;
int pref_syncref;
char *autosync_ref;
int x;
status = hdspe_read(hdspe, HDSPE_RD_STATUS0);
status2 = hdspe_read(hdspe, HDSPE_RD_STATUS2);
timecode = hdspe_read(hdspe, HDSPE_RD_FBITS);
snd_iprintf(buffer, "%s (Card #%d) Rev.%x\n",
hdspe->card_name, hdspe->card->number + 1,
hdspe->firmware_rev);
snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
hdspe->irq, hdspe->port, (unsigned long)hdspe->iobase);
snd_iprintf(buffer, "--- System ---\n");
snd_iprintf(buffer,
"IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
status & HDSPE_audioIRQPending,
(status & HDSPE_midi0IRQPending) ? 1 : 0,
(status & HDSPE_midi1IRQPending) ? 1 : 0,
hdspe->irq_count);
snd_iprintf(buffer,
"HW pointer: id = %d, rawptr = %d (%d->%d) "
"estimated= %ld (bytes)\n",
((status & HDSPE_BufferID) ? 1 : 0),
(status & HDSPE_BufferPositionMask),
(status & HDSPE_BufferPositionMask) %
(2 * (int)hdspe->period_bytes),
((status & HDSPE_BufferPositionMask) - 64) %
(2 * (int)hdspe->period_bytes),
(long) hdspe_hw_pointer(hdspe) * 4);
snd_iprintf(buffer,
"MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
hdspe_read(hdspe, HDSPE_midiStatusOut0) & 0xFF,
hdspe_read(hdspe, HDSPE_midiStatusOut1) & 0xFF,
hdspe_read(hdspe, HDSPE_midiStatusIn0) & 0xFF,
hdspe_read(hdspe, HDSPE_midiStatusIn1) & 0xFF);
snd_iprintf(buffer,
"MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
hdspe_read(hdspe, HDSPE_midiStatusIn2) & 0xFF,
hdspe_read(hdspe, HDSPE_midiStatusOut2) & 0xFF);
snd_iprintf(buffer,
"Register: ctrl1=0x%x, status0=0x%x, "
"status2=0x%x\n",
hdspe->reg.control.raw,
status, status2);
snd_iprintf(buffer, "--- Settings ---\n");
x = hdspe_period_size(hdspe);
snd_iprintf(buffer,
"Size (Latency): %d samples (2 periods of %lu bytes)\n",
x, (unsigned long) hdspe->period_bytes);
snd_iprintf(buffer, "Line out: %s\n",
(hdspe->
reg.control.raw & HDSPE_LineOut) ? "on " : "off");
snd_iprintf(buffer,
"ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
(hdspe->
reg.control.raw & HDSPE_clr_tms) ? "on" : "off",
(hdspe->
reg.control.raw & HDSPE_Emphasis) ? "on" : "off",
(hdspe->
reg.control.raw & HDSPE_Dolby) ? "on" : "off");
pref_syncref = hdspe_aes_get_preferred_sync_ref(hdspe);
snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
HDSPE_CLOCK_SOURCE_NAME(HDSPE_AES, pref_syncref));
snd_iprintf(buffer, "System Clock Frequency: %d\n",
hdspe->system_sample_rate);
snd_iprintf(buffer, "Double speed: %s\n",
hdspe->reg.control.raw & HDSPE_DS_DoubleWire?
"Double wire" : "Single wire");
snd_iprintf(buffer, "Quad speed: %s\n",
hdspe->reg.control.raw & HDSPE_QS_DoubleWire?
"Double wire" :
hdspe->reg.control.raw & HDSPE_QS_QuadWire?
"Quad wire" : "Single wire");
snd_iprintf(buffer, "--- Status:\n");
wcLock = status & HDSPE_AES_wcLock;
wcSync = wcLock && (status & HDSPE_AES_wcSync);
snd_iprintf(buffer, "Word: %s Frequency: %d\n",
(wcLock) ? (wcSync ? "Sync " : "Lock ") : "No Lock",
hdspe_freq_sample_rate(HDSPE_AES_wclk_freq(status)));
for (x = 0; x < 8; x++) {
snd_iprintf(buffer, "AES%d: %s Frequency: %d\n",
x+1,
(status2 & (HDSPE_LockAES >> x)) ?
"Sync " : "No Lock",
hdspe_freq_sample_rate((timecode >> (4*x)) & 0xF));
}
switch (hdspe_aes_get_autosync_ref(hdspe)) {
case HDSPE_CLOCK_SOURCE_INTERN:
autosync_ref = "None"; break;
case HDSPE_CLOCK_SOURCE_WORD:
autosync_ref = "Word Clock"; break;
case HDSPE_CLOCK_SOURCE_AES1:
autosync_ref = "AES1"; break;
case HDSPE_CLOCK_SOURCE_AES2:
autosync_ref = "AES2"; break;
case HDSPE_CLOCK_SOURCE_AES3:
autosync_ref = "AES3"; break;
case HDSPE_CLOCK_SOURCE_AES4:
autosync_ref = "AES4"; break;
case HDSPE_CLOCK_SOURCE_AES5:
autosync_ref = "AES5"; break;
case HDSPE_CLOCK_SOURCE_AES6:
autosync_ref = "AES6"; break;
case HDSPE_CLOCK_SOURCE_AES7:
autosync_ref = "AES7"; break;
case HDSPE_CLOCK_SOURCE_AES8:
autosync_ref = "AES8"; break;
case HDSPE_CLOCK_SOURCE_TCO:
autosync_ref = "TCO"; break;
case HDSPE_CLOCK_SOURCE_SYNC_IN:
autosync_ref = "Sync In"; break;
default:
autosync_ref = "---"; break;
}
snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
snd_iprintf(buffer, "\n");
#endif /*OLDSTUFF*/
}
static const struct hdspe_methods hdspe_aes_methods = {
.get_card_info = hdspe_get_card_info,
.read_status = hdspe_aes_read_status,
.get_float_format = hdspe_aes_get_float_format,
.set_float_format = hdspe_aes_set_float_format,
.read_proc = hdspe_aes_proc_read,
#ifdef OLDSTUFF
.get_freq = hdspe_aes_get_freq,
.get_external_freq = hdspe_aes_get_external_freq,
#endif /*OLDSTUFF*/
.get_autosync_ref = hdspe_aes_get_autosync_ref,
.get_clock_mode = hdspe_aes_get_clock_mode,
.set_clock_mode = hdspe_aes_set_clock_mode,
.get_pref_sync_ref = hdspe_aes_get_preferred_sync_ref,
.set_pref_sync_ref = hdspe_aes_set_preferred_sync_ref,
#ifdef OLDSTUFF
.get_sync_status = hdspe_aes_get_sync_status,
.has_status_changed = hdspe_aes_has_status_changed
#endif /*OLDSTUFF*/
};
static const struct hdspe_tables hdspe_aes_tables = {
.ss_in_channels = AES_CHANNELS,
.ss_out_channels = AES_CHANNELS,
.ds_in_channels = AES_CHANNELS,
.ds_out_channels = AES_CHANNELS,
.qs_in_channels = AES_CHANNELS,
.qs_out_channels = AES_CHANNELS,
.channel_map_in_ss = channel_map_aes,
.channel_map_out_ss = channel_map_aes,
.channel_map_in_ds = channel_map_aes,
.channel_map_out_ds = channel_map_aes,
.channel_map_in_qs = channel_map_aes,
.channel_map_out_qs = channel_map_aes,
.port_names_in_ss = texts_ports_aes,
.port_names_out_ss = texts_ports_aes,
.port_names_in_ds = texts_ports_aes,
.port_names_out_ds = texts_ports_aes,
.port_names_in_qs = texts_ports_aes,
.port_names_out_qs = texts_ports_aes,
.clock_source_names = hdspe_aes_clock_source_names
};
static struct hdspe_midi hdspe_aes_midi_ports[3] = {
{.portname = "MIDI 1",
.dataIn = HDSPE_midiDataIn0,
.statusIn = HDSPE_midiStatusIn0,
.dataOut = HDSPE_midiDataOut0,
.statusOut = HDSPE_midiStatusOut0,
.ie = HDSPE_Midi0InterruptEnable,
.irq = HDSPE_midi0IRQPending,
},
{.portname = "MIDI 2",
.dataIn = HDSPE_midiDataIn1,
.statusIn = HDSPE_midiStatusIn1,
.dataOut = HDSPE_midiDataOut1,
.statusOut = HDSPE_midiStatusOut1,
.ie = HDSPE_Midi1InterruptEnable,
.irq = HDSPE_midi1IRQPending,
},
{.portname = "MTC",
.dataIn = HDSPE_midiDataIn2,
.statusIn = HDSPE_midiStatusIn2,
.dataOut = -1,
.statusOut = -1,
.ie = HDSPE_Midi2InterruptEnable,
.irq = HDSPE_midi2IRQPendingAES,
}
};
int hdspe_init_aes(struct hdspe* hdspe)
{
hdspe->reg.control.aes.Master = true;
// hdspe->reg.control.aes.SyncRef0 = 1; // preferred sync is AES1
hdspe->reg.control.aes.PRO = true; // Professional mode
#ifdef FROM_WIN_DRIVER
// TODO
if (deviceExtension->Emphasis) deviceExtension->Register |= EMP;
else deviceExtension->Register &= ~EMP;
if (deviceExtension->Professional) deviceExtension->Register |= PRO;
else deviceExtension->Register &= ~PRO;
if (deviceExtension->NonAudio) deviceExtension->Register |= Dolby;
else deviceExtension->Register &= ~Dolby;
if (deviceExtension->DoubleSpeed) deviceExtension->Register |= DS_DoubleWire;
else deviceExtension->Register &= ~DS_DoubleWire;
deviceExtension->Register &= ~(QS_DoubleWire+QS_QuadWire);
switch (deviceExtension->QuadSpeed) {
case 1: deviceExtension->Register |= QS_DoubleWire; break;
case 2: deviceExtension->Register |= QS_QuadWire; break;
}
if (deviceExtension->Frame) deviceExtension->Register |= MADI_WCK48;
else deviceExtension->Register &= ~MADI_WCK48;
if (deviceExtension->InputSource) deviceExtension->Register |= WCK_TERM;
else deviceExtension->Register &= ~WCK_TERM;
if (deviceExtension->FormatAC3 && deviceExtension->DeviceType != kRPM && deviceExtension->DeviceType != kHDSP_MADI) {
deviceExtension->Register |= Dolby;
deviceExtension->Register &= ~LineOut;
}
#endif
hdspe_write_control(hdspe);
hdspe->m = hdspe_aes_methods;
hdspe->card_name = "RME AES32";
hdspe_init_midi(hdspe, 2 + (hdspe->tco ? 1 : 0), hdspe_aes_midi_ports);
hdspe->t = hdspe_aes_tables;
hdspe_init_autosync_tables(
hdspe, ARRAY_SIZE(aes_autosync_ref), aes_autosync_ref);
return 0;
}
void hdspe_terminate_aes(struct hdspe* hdspe)
{
#ifdef FROM_WIN_DRIVER
// TODO
if (!deviceExtension->NonAudio && deviceExtension->DeviceType != kRPM && deviceExtension->DeviceType != kHDSP_MADI)
deviceExtension->Register &= ~Dolby;
if (deviceExtension->FormatAC3)
deviceExtension->Register |= LineOut;
#endif
}
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_common.c 0000664 0000000 0000000 00000030750 15060370513 0022167 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* hdspe_common.c
* @brief RME HDSPe cards common driver methods.
*
* 20210727,28,29 - Philippe.Bekaert@uhasselt.be
*
* Based on earlier work of the other MODULE_AUTHORS,
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
#include "hdspe.h"
#include "hdspe_core.h"
#include
#ifdef FROM_WIN_DRIVER
//TODO
// Loopback
void hwRecordOutput(PDEVICE_EXTENSION deviceExtension, int channel, int value)
{
TRACE(TL_TRACE, ("hwRecordOutput: channel=%d, value=%d, numChannels=%d\n", channel, value, deviceExtension->NumChannels));
if (channel < deviceExtension->NumChannels) {
deviceExtension->RecordOutput[channel] = value;
if (deviceExtension->DeviceType < kHDSP_AES) {
WriteRegister(deviceExtension, RECORD_OUTPUT+channel, value);
} else {
TRACE(TL_TRACE, ("...hwRecordOutput MADI\n"));
WriteRegister(deviceExtension, MADI_RECORD_OUTPUT+channel, value);
}
}
}
#endif
u32 hdspe_freq_sample_rate(enum hdspe_freq f)
{
static u32 rates[HDSPE_FREQ_COUNT] = {
HDSPE_FREQ_SAMPLE_RATE(0),
HDSPE_FREQ_SAMPLE_RATE(1),
HDSPE_FREQ_SAMPLE_RATE(2),
HDSPE_FREQ_SAMPLE_RATE(3),
HDSPE_FREQ_SAMPLE_RATE(4),
HDSPE_FREQ_SAMPLE_RATE(5),
HDSPE_FREQ_SAMPLE_RATE(6),
HDSPE_FREQ_SAMPLE_RATE(7),
HDSPE_FREQ_SAMPLE_RATE(8),
};
return (f >= 0 && f < HDSPE_FREQ_COUNT) ? rates[f] : 0;
}
/* Get the speed mode reflecting the sample rate f */
static enum hdspe_speed hdspe_sample_rate_speed_mode(u32 rate)
{
if (rate < 56000)
return HDSPE_SPEED_SINGLE;
else if (rate < 112000)
return HDSPE_SPEED_DOUBLE;
else
return HDSPE_SPEED_QUAD;
}
/* Get the speed mode encoded in the frequency class */
static enum hdspe_speed hdspe_freq_speed(enum hdspe_freq f)
{
switch (f) {
case 1: case 2: case 3:
return HDSPE_SPEED_SINGLE;
case 4: case 5: case 6:
return HDSPE_SPEED_DOUBLE;
case 7: case 8: case 9:
return HDSPE_SPEED_QUAD;
default:
snd_BUG();
return HDSPE_SPEED_INVALID;
};
}
/* Get the frequency class best representing the given rate */
enum hdspe_freq hdspe_sample_rate_freq(u32 rate)
{
enum hdspe_freq f;
int speed_coef = 0;
if (rate >= 112000) {
rate /= 4;
speed_coef = 6;
} else if (rate >= 56000) {
rate /= 2;
speed_coef = 3;
}
if (rate < 38050)
f = HDSPE_FREQ_32KHZ;
else if (rate < 46050)
f = HDSPE_FREQ_44_1KHZ;
else
f = HDSPE_FREQ_48KHZ;
return f + speed_coef;
}
/* Converts frequency class to speed mode */
enum hdspe_freq hdspe_speed_adapt(enum hdspe_freq f, enum hdspe_speed speed_mode)
{
switch (f) {
case 0: break; /* This happens when autosync source looses sync e.g. */
case 1: case 2: case 3:
switch (speed_mode) {
case HDSPE_SPEED_DOUBLE: f += 3; break;
case HDSPE_SPEED_QUAD : f += 6; break;
default: {}
}
break;
case 4: case 5: case 6:
switch (speed_mode) {
case HDSPE_SPEED_SINGLE: f -= 3; break;
case HDSPE_SPEED_QUAD : f += 3; break;
default: {}
}
break;
case 7: case 8: case 9:
switch (speed_mode) {
case HDSPE_SPEED_SINGLE: f -= 6; break;
case HDSPE_SPEED_DOUBLE: f -= 3; break;
default: {}
}
break;
default:
snd_BUG();
return HDSPE_FREQ_INVALID;
}
return f;
}
/* Adapt the sample rate to the given speed mode */
static u32 hdspe_sample_rate_adapt(u32 rate, enum hdspe_speed speed_mode)
{
switch (hdspe_sample_rate_speed_mode(rate)) {
case HDSPE_SPEED_SINGLE:
switch (speed_mode) {
case HDSPE_SPEED_DOUBLE: rate *= 2; break;
case HDSPE_SPEED_QUAD : rate *= 4; break;
default: {}
}
break;
case HDSPE_SPEED_DOUBLE:
switch (speed_mode) {
case HDSPE_SPEED_SINGLE: rate /= 2; break;
case HDSPE_SPEED_QUAD : rate *= 2; break;
default: {}
}
break;
case HDSPE_SPEED_QUAD:
switch (speed_mode) {
case HDSPE_SPEED_SINGLE: rate /= 4; break;
case HDSPE_SPEED_DOUBLE: rate /= 2; break;
default: {}
}
break;
default:
snd_BUG();
}
return rate;
}
/* Get the current speed mode */
enum hdspe_speed hdspe_speed_mode(struct hdspe* hdspe)
{
struct hdspe_control_reg_common control = hdspe->reg.control.common;
return control.qs ? HDSPE_SPEED_QUAD
: control.ds ? HDSPE_SPEED_DOUBLE
: HDSPE_SPEED_SINGLE;
}
int hdspe_speed_factor(struct hdspe* hdspe)
{
struct hdspe_control_reg_common control = hdspe->reg.control.common;
return control.qs ? 4 : control.ds ? 2 : 1;
}
/* Get the current internal frequency class */
enum hdspe_freq hdspe_internal_freq(struct hdspe* hdspe)
{
struct hdspe_control_reg_common control = hdspe->reg.control.common;
return control.freq + (control.qs ? 6 : control.ds ? 3 : 0);
}
/* Write the internal frequency (single speed frequency and speed
* mode control register bits). */
int hdspe_write_internal_freq(struct hdspe* hdspe, enum hdspe_freq f)
{
enum hdspe_freq single_speed_freq =
hdspe_speed_adapt(f, HDSPE_SPEED_SINGLE);
enum hdspe_speed speed_mode =
hdspe_freq_speed(f);
dev_dbg(hdspe->card->dev, "%s(%d)\n", __func__, f);
if (f == hdspe_internal_freq(hdspe))
return false;
hdspe->reg.control.common.freq = single_speed_freq;
hdspe->reg.control.common.ds = (speed_mode == HDSPE_SPEED_DOUBLE);
hdspe->reg.control.common.qs = (speed_mode == HDSPE_SPEED_QUAD);
hdspe_write_control(hdspe);
/* Update TCO module "app" sample rate */
if (hdspe->tco)
hdspe_tco_set_app_sample_rate(hdspe);
return true;
}
/* PLL reference frequency constants */
static u64 freq_const[HDSPE_IO_TYPE_COUNT] = {
110069313433624ULL, // MADI
131072000000000ULL, // MADIface
104857600000000ULL, // AIO
110069313433624ULL, // AES
104857600000000ULL, // RayDAT
104857600000000ULL // AIO Pro
};
/* Convert DDS register period to Parts Pro Milion pitch value,
* relative to the control registers single speed internal frequency. */
static int hdspe_dds2ppm(struct hdspe* hdspe, u32 dds)
{
// ppm = 1000000 * refdds / dds - 1000000
// refdds = fconst / refrate
struct hdspe_control_reg_common control = hdspe->reg.control.common;
u32 refrate = hdspe_freq_sample_rate(control.freq);
u64 fconst = freq_const[hdspe->io_type];
u64 refdds = 1000000ULL * div_u64(fconst, refrate);
snd_BUG_ON(dds == 0);
return dds != 0 ? (int)div_u64(refdds, dds) : 1000000; // - 1000000;
}
/* Convert Parts Pro Milion pitch value relative to the control registers
* single speed internal frequency to DDS register period. */
static u32 hdspe_ppm2dds(struct hdspe* hdspe, int ppm)
{
// dds = 1000000 * refdds / (1000000 + ppm)
// refdds = fconst / refrate
struct hdspe_control_reg_common control = hdspe->reg.control.common;
u32 refrate = hdspe_freq_sample_rate(control.freq);
u64 fconst = freq_const[hdspe->io_type];
u64 refdds = div_u64(fconst, refrate);
u64 refddsM = 1000000ULL * refdds;
/* snd_BUG_ON(ppm == 0); */
return ppm != 0 ? (u32)div_u64(refddsM, ppm) : refdds;
}
/* Convert DDS value to sample rate, taking into account the current speed
* mode. */
static u32 hdspe_dds_sample_rate(struct hdspe* hdspe, u32 dds)
{
struct hdspe_control_reg_common control = hdspe->reg.control.common;
u64 fconst = freq_const[hdspe->io_type] *
(control.qs ? 4 : control.ds ? 2 : 1);
/* snd_BUG_ON(dds == 0); not a bug here, but need to catch it */
return dds != 0 ? (u32)div_u64(fconst, dds)
: hdspe_freq_sample_rate(control.freq);
}
void hdspe_dds_range(struct hdspe* hdspe, u32* min, u32* max)
{
u64 fconst = freq_const[hdspe->io_type];
*min = (u32)div_u64(fconst, 51750); /* 207KHz / 4 */
*max = (u32)div_u64(fconst, 27000);
}
u32 hdspe_get_dds(struct hdspe* hdspe)
{
return le32_to_cpu(hdspe->reg.pll_freq);
}
int hdspe_write_dds(struct hdspe* hdspe, u32 dds)
{
__le32 dds_le = cpu_to_le32(dds);
u32 ddsmin, ddsmax;
int rc = 0;
hdspe_dds_range(hdspe, &ddsmin, &ddsmax);
if (dds < ddsmin || dds > ddsmax) {
rc = -EINVAL;
goto done;
}
if (dds_le == hdspe->reg.pll_freq) {
rc = 0;
goto done;
}
hdspe->reg.pll_freq = dds_le;
hdspe_write_pll_freq(hdspe);
rc = 1;
done:
dev_dbg(hdspe->card->dev, "%s() dds = %u sample_rate = %u rc = %d.\n",
__func__, dds, hdspe_dds_sample_rate(hdspe, dds), rc);
return rc;
}
u32 hdspe_internal_pitch(struct hdspe* hdspe)
{
return hdspe_dds2ppm(hdspe, hdspe_get_dds(hdspe));
}
int hdspe_write_internal_pitch(struct hdspe* hdspe, int ppm)
{
return hdspe_write_dds(hdspe, hdspe_ppm2dds(hdspe, ppm));
}
u32 hdspe_read_system_pitch(struct hdspe* hdspe)
{
return hdspe_dds2ppm(hdspe, hdspe_read_pll_freq(hdspe));
}
u32 hdspe_read_system_sample_rate(struct hdspe* hdspe)
{
return hdspe_dds_sample_rate(hdspe, hdspe_read_pll_freq(hdspe));
}
void hdspe_read_sample_rate_status(struct hdspe* hdspe,
struct hdspe_status* status)
{
struct hdspe_control_reg_common control = hdspe->reg.control.common;
status->sample_rate_numerator = freq_const[hdspe->io_type] *
(control.qs ? 4 : control.ds ? 2 : 1);
status->sample_rate_denominator = hdspe_read_pll_freq(hdspe);
status->internal_sample_rate_denominator =
le32_to_cpu(hdspe->reg.pll_freq);
status->buffer_size = hdspe_period_size(hdspe);
status->running = hdspe->running;
status->capture_pid = hdspe->capture_pid;
status->playback_pid = hdspe->playback_pid;
}
static int hdspe_write_system_sample_rate(struct hdspe* hdspe, u32 rate)
{
int changed = false;
u32 single_speed_rate =
hdspe_sample_rate_adapt(rate, HDSPE_SPEED_SINGLE);
enum hdspe_freq freq =
hdspe_sample_rate_freq(rate);
u64 dds = div_u64(freq_const[hdspe->io_type], single_speed_rate);
dev_dbg(hdspe->card->dev, "%s(%d) ...\n", __func__, rate);
changed = hdspe_write_internal_freq(hdspe, freq);
/* dds should be less than 2^32 for being written to FREQ register */
snd_BUG_ON(dds >> 32);
if (hdspe_write_dds(hdspe, dds))
changed = true;
return changed;
}
void hdspe_set_channel_map(struct hdspe* hdspe, enum hdspe_speed speed)
{
dev_dbg(hdspe->card->dev, "%s()\n", __func__);
switch (speed) {
case HDSPE_SPEED_SINGLE:
hdspe->channel_map_in = hdspe->t.channel_map_in_ss;
hdspe->channel_map_out = hdspe->t.channel_map_out_ss;
hdspe->max_channels_in = hdspe->t.ss_in_channels;
hdspe->max_channels_out = hdspe->t.ss_out_channels;
hdspe->port_names_in = hdspe->t.port_names_in_ss;
hdspe->port_names_out = hdspe->t.port_names_out_ss;
break;
case HDSPE_SPEED_DOUBLE:
hdspe->channel_map_in = hdspe->t.channel_map_in_ds;
hdspe->channel_map_out = hdspe->t.channel_map_out_ds;
hdspe->max_channels_in = hdspe->t.ds_in_channels;
hdspe->max_channels_out = hdspe->t.ds_out_channels;
hdspe->port_names_in = hdspe->t.port_names_in_ds;
hdspe->port_names_out = hdspe->t.port_names_out_ds;
break;
case HDSPE_SPEED_QUAD:
hdspe->channel_map_in = hdspe->t.channel_map_in_qs;
hdspe->channel_map_out = hdspe->t.channel_map_out_qs;
hdspe->max_channels_in = hdspe->t.qs_in_channels;
hdspe->max_channels_out = hdspe->t.qs_out_channels;
hdspe->port_names_in = hdspe->t.port_names_in_qs;
hdspe->port_names_out = hdspe->t.port_names_out_qs;
break;
default: {}
};
hdspe_mixer_update_channel_map(hdspe);
}
int hdspe_set_sample_rate(struct hdspe * hdspe, u32 desired_rate)
{
/* TODO:
Changing between Single, Double and Quad speed is not
allowed if any substreams are open. This is because such a change
causes a shift in the location of the DMA buffers and a reduction
in the number of available buffers.
Note that a similar but essentially insoluble problem exists for
externally-driven rate changes. All we can do is to flag rate
changes in the read/write routines.
*/
int changed;
enum hdspe_speed desired_speed_mode =
hdspe_sample_rate_speed_mode(desired_rate);
dev_dbg(hdspe->card->dev, "%s(%d)\n", __func__, desired_rate);
#ifdef NEVER
/* locks up ubuntu desktop if we do that here. */
if (!snd_hdspe_use_is_exclusive(hdspe)) {
dev_warn(hdspe->card->dev, "Cannot change sample rate: no exclusive use.\n");
return -EBUSY;
}
#endif /*NEVER*/
#ifdef NEVER
/* This prevents sample rate changes also in cases where they
* are perfectly fine. */
if (desired_speed_mode != current_speed_mode) {
if (hdspe->capture_pid >= 0 || hdspe->playback_pid >= 0) {
dev_err(hdspe->card->dev,
"Cannot change from %s speed to %s speed mode"
"(capture PID = %d, playback PID = %d)\n",
HDSPE_SPEED_NAME(current_speed_mode),
HDSPE_SPEED_NAME(desired_speed_mode),
hdspe->capture_pid, hdspe->playback_pid);
return -EBUSY;
}
}
#endif /*NEVER*/
changed = hdspe_write_system_sample_rate(hdspe, desired_rate);
/* if (changed) */
hdspe_set_channel_map(hdspe, desired_speed_mode);
if (changed) {
/* notify Internal Frequency and DDS control elements */
HDSPE_CTL_NOTIFY(internal_freq);
HDSPE_CTL_NOTIFY(dds);
}
return changed;
}
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_control.c 0000664 0000000 0000000 00000070132 15060370513 0022355 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* hdspe-control.c
* @brief RME HDSPe sound card driver status and control interface.
*
* 20210727,28,29,30,0908,09,10,20220330 - Philippe.Bekaert@uhasselt.be
*
* Based on earlier work of the other MODULE_AUTHORS,
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
/* TODO: systematic value range checking */
#include "hdspe.h"
#include "hdspe_core.h"
#include "hdspe_control.h"
/**
* hdspe_init_autosync_tables: calculates tables needed for the
* preferred sync and autosync ref properties below, given the list of
* autosync clock sources for a card:
* Tables calculated:
* - hdspe::t.autosync_texts : a text label for each clock source,
* plus HDSPE_CLOCK_SOURCE_INTERN at the end of the list.
* - hdspe::t.autosync_idx2ref : maps a property index to a
* hdspe_clock_source enum.
* - hdspe::t.autosync_ref2idx : maps a hdspe_clock_source enum to a
* property index.
* The number of clock sources, including HDSPE_CLOCK_SOURCE_INTERN,
* is stored in hdspe::t.autosync_count.
* @nr_autosync_opts: the number of clock sources (size of autosync_opts array)
* @autosync_opts: the autosync clock sources offered by the card. */
void hdspe_init_autosync_tables(struct hdspe* hdspe,
int nr_autosync_opts,
enum hdspe_clock_source* autosync_opts)
{
int i, n;
struct hdspe_tables *t = &hdspe->t;
/* Clear the tables to sane defaults. */
for (i=0; iautosync_texts[i] = "";
t->autosync_idx2ref[i] = 0;
t->autosync_ref2idx[i] = 0;
}
for (i=0, n=0; iautosync_texts[n] = hdspe_clock_source_name(hdspe, ref);
t->autosync_idx2ref[n] = ref;
t->autosync_ref2idx[ref] = n;
n++;
}
// Add HDSPE_CLOCK_SOURCE_INTERN as the last option
// for Current AutoSync Reference property.
t->autosync_texts[n] = hdspe_clock_source_name(hdspe,
HDSPE_CLOCK_SOURCE_INTERN);
t->autosync_idx2ref[n] = HDSPE_CLOCK_SOURCE_INTERN;
t->autosync_ref2idx[HDSPE_CLOCK_SOURCE_INTERN] = n;
n++;
t->autosync_count = n;
dev_dbg(hdspe->card->dev, "AutoSync tables: %d clock sources:\n", t->autosync_count);
for (i=0; icard->dev, "Idx %2d idx2ref=%d texts='%s'\n",
i, t->autosync_idx2ref[i],
t->autosync_texts[i]);
}
for (i=0; icard->dev, "Ref %2d '%s' ref2idx=%d\n",
i, hdspe_clock_source_name(hdspe, i),
t->autosync_ref2idx[i]);
}
}
const char* const hdspe_clock_source_name(struct hdspe* hdspe, int i)
{
return (i >= 0 && i < HDSPE_CLOCK_SOURCE_COUNT)
? hdspe->t.clock_source_names[i] : "???";
}
/* ------------------------------------------------------- */
int hdspe_control_get(struct snd_kcontrol *kcontrol,
int (*get)(struct hdspe*),
bool lock_req, const char* propname)
{
int val;
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
dev_dbg(hdspe->card->dev, "hdspe_control_get(%s) ...\n", propname);
if (lock_req) spin_lock_irq(&hdspe->lock);
val = get(hdspe);
if (lock_req) spin_unlock_irq(&hdspe->lock);
dev_dbg(hdspe->card->dev, "... = %d.\n", val);
return val;
}
int hdspe_control_put(struct snd_kcontrol *kcontrol, int val,
int (*get)(struct hdspe*),
int (*put)(struct hdspe*, int val),
bool lock_req, bool excl_req, const char* propname)
{
int oldval = -1, changed = 0, rc = 0;
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
if (excl_req && !snd_hdspe_use_is_exclusive(hdspe)) {
dev_dbg(hdspe->card->dev,
"snd_hdspe_put(%s,%d): no exclusive access!\n",
propname, val);
return -EBUSY;
}
dev_dbg(hdspe->card->dev, "snd_hdspe_put(%s,%d) %s get() ...\n",
propname, val, get ? "with" : "without");
if (lock_req) spin_lock_irq(&hdspe->lock);
oldval = get ? get(hdspe) : val;
changed = (val != oldval);
if (!get || changed)
rc = put(hdspe, val);
if (lock_req) spin_unlock_irq(&hdspe->lock);
dev_dbg(hdspe->card->dev,
"... val = %d, oldval = %d, changed = %d, put rc = %d.\n",
val, oldval, changed, rc);
return rc ? rc : changed;
}
/* -------------- status polling ------------------- */
HDSPE_RW_INT1_HDSPE_METHODS(status_polling, 0, HZ, 1)
void hdspe_status_work(struct work_struct *work)
{
struct hdspe *hdspe = container_of(work, struct hdspe, status_work);
int64_t sr_delta;
int i;
bool changed = false;
struct hdspe_status o = hdspe->last_status;
struct hdspe_status n;
hdspe->m.read_status(hdspe, &n);
for (i = 0; i < HDSPE_CLOCK_SOURCE_COUNT; i++) {
if (n.sync[i] != o.sync[i]) {
dev_dbg(hdspe->card->dev,
"sync source %d status changed %d -> %d.\n",
i, o.sync[i], n.sync[i]);
HDSPE_CTL_NOTIFY(autosync_status);
changed = true;
break;
}
}
for (i = 0; i < HDSPE_CLOCK_SOURCE_COUNT; i++) {
if (n.freq[i] != o.freq[i]) {
dev_dbg(hdspe->card->dev,
"sync source %d freq changed %d -> %d.\n",
i, o.freq[i], n.freq[i]);
HDSPE_CTL_NOTIFY(autosync_freq);
changed = true;
break;
}
}
if (n.autosync_ref != o.autosync_ref) {
dev_dbg(hdspe->card->dev, "autosync ref changed %d -> %d.\n",
o.autosync_ref, n.autosync_ref);
HDSPE_CTL_NOTIFY(autosync_ref);
changed = true;
}
sr_delta = (int64_t)n.sample_rate_denominator
- (int64_t)o.sample_rate_denominator;
if (n.sample_rate_numerator != o.sample_rate_numerator ||
(sr_delta<0 ? -sr_delta : sr_delta) >
(n.sample_rate_denominator / 1000000)) {
dev_dbg(hdspe->card->dev,
"sample rate changed %llu/%u -> %llu/%u.\n",
o.sample_rate_numerator, o.sample_rate_denominator,
n.sample_rate_numerator, n.sample_rate_denominator);
HDSPE_CTL_NOTIFY(raw_sample_rate);
changed = true;
}
if (hdspe->m.check_status_change &&
hdspe->m.check_status_change(hdspe, &o, &n))
changed = true;
if (hdspe->tco && hdspe_tco_notify_status_change(hdspe)) {
changed = true;
}
hdspe->last_status = n;
if (hdspe->last_status_change_jiffies == 0)
hdspe->last_status_change_jiffies = jiffies;
if (changed) {
hdspe->last_status_change_jiffies = 0;
hdspe->status_polling = 0; /* disable - user must re-enable */
HDSPE_CTL_NOTIFY(status_polling);
} else if (jiffies > hdspe->last_status_change_jiffies + 2*HZ) {
dev_dbg(hdspe->card->dev,
"%s: polling timeout expired: jiffies=%lu, last_status_change_jiffied=%lu, delta=%ld, 2*HZ=%d.\n", __func__,
jiffies, hdspe->last_status_change_jiffies,
jiffies - hdspe->last_status_change_jiffies, 2*HZ);
hdspe->last_status_change_jiffies = 0;
hdspe->status_polling = 0; /* disable - user must re-enable */
HDSPE_CTL_NOTIFY(status_polling);
}
}
/* ------------------ raw sample rate and DDS -------------------- */
static int snd_hdspe_info_raw_sample_rate(struct snd_kcontrol* kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER64;
uinfo->count = 2;
return 0;
}
static int snd_hdspe_get_raw_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
struct hdspe_status s;
hdspe_read_sample_rate_status(hdspe, &s);
ucontrol->value.integer64.value[0] = s.sample_rate_numerator;
ucontrol->value.integer64.value[1] = s.sample_rate_denominator;
return 0;
}
static int snd_hdspe_info_dds(struct snd_kcontrol* kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
u32 ddsmin, ddsmax;
hdspe_dds_range(hdspe, &ddsmin, &ddsmax);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ddsmin;
uinfo->value.integer.max = ddsmax;
return 0;
}
static int hdspe_write_dds_i(struct hdspe* hdspe, int val)
{
return hdspe_write_dds(hdspe, (u32)val);
}
HDSPE_INT1_GET(dds, hdspe_get_dds, true)
HDSPE_INT1_PUT(dds, NULL, hdspe_write_dds_i, true, false)
/* -------------- system clock mode ----------------- */
static int snd_hdspe_info_clock_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[] = {
HDSPE_CLOCK_MODE_NAME(0),
HDSPE_CLOCK_MODE_NAME(1)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
static int hdspe_get_clock_mode(struct hdspe* hdspe)
{
return hdspe->m.get_clock_mode(hdspe);
}
static int hdspe_set_clock_mode(struct hdspe* hdspe, int val)
{
hdspe->m.set_clock_mode(hdspe, val);
return 0;
}
HDSPE_RW_ENUM_METHODS(clock_mode,
hdspe_get_clock_mode, hdspe_set_clock_mode, false);
/* --------------- preferred sync reference ------------------ */
static int snd_hdspe_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
// Last autosync option ("intern") does not apply here.
snd_ctl_enum_info(uinfo, 1, hdspe->t.autosync_count-1,
hdspe->t.autosync_texts);
return 0;
}
static int hdspe_get_pref_sync_ref_idx(struct hdspe* hdspe)
{
return hdspe->t.autosync_ref2idx[hdspe->m.get_pref_sync_ref(hdspe)];
}
static int hdspe_put_pref_sync_ref_idx(struct hdspe* hdspe, int idx)
{
hdspe->m.set_pref_sync_ref(hdspe, hdspe->t.autosync_idx2ref[idx]);
return 0;
}
HDSPE_RW_ENUM_METHODS(pref_sync_ref,
hdspe_get_pref_sync_ref_idx, hdspe_put_pref_sync_ref_idx,
false);
/* --------------- AutoSync reference ----------------- */
static int snd_hdspe_info_autosync_ref(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
snd_ctl_enum_info(uinfo, 1, hdspe->t.autosync_count,
hdspe->t.autosync_texts);
return 0;
}
static int hdspe_get_autosync_ref_idx(struct hdspe* hdspe)
{
return hdspe->t.autosync_ref2idx[hdspe->m.get_autosync_ref(hdspe)];
}
HDSPE_RO_ENUM_METHODS(autosync_ref, hdspe_get_autosync_ref_idx);
/* --------------- AutoSync Status ------------------- */
static int snd_hdspe_info_autosync_status(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
static const char *const texts[] = {
HDSPE_SYNC_STATUS_NAME(0),
HDSPE_SYNC_STATUS_NAME(1),
HDSPE_SYNC_STATUS_NAME(2),
HDSPE_SYNC_STATUS_NAME(3)
};
ENUMERATED_CTL_INFO(uinfo, texts);
uinfo->count = hdspe->t.autosync_count - 1; /* last item is "internal" */
return 0;
}
static int snd_hdspe_get_autosync_status(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspe* hdspe = snd_kcontrol_chip(kcontrol);
int i;
struct hdspe_status s;
hdspe->m.read_status(hdspe, &s);
for (i=0; it.autosync_count-1; i++) {
int ref = hdspe->t.autosync_idx2ref[i];
ucontrol->value.enumerated.item[i] = s.sync[ref];
}
return 0;
}
/* -------------- AutoSync Frequency ------------ */
static const char *const texts_freq[HDSPE_FREQ_COUNT] = {
HDSPE_FREQ_NAME(HDSPE_FREQ_NO_LOCK),
HDSPE_FREQ_NAME(HDSPE_FREQ_32KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_44_1KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_48KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_64KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_88_2KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_96KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_128KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_176_4KHZ),
HDSPE_FREQ_NAME(HDSPE_FREQ_192KHZ)
};
const char* const hdspe_freq_name(enum hdspe_freq i)
{
return (i >= 0 && i < HDSPE_FREQ_COUNT)
? texts_freq[i] : "???";
}
static int snd_hdspe_info_autosync_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol);
ENUMERATED_CTL_INFO(uinfo, texts_freq);
uinfo->count = hdspe->t.autosync_count - 1; /* skip last item "internal" */
return 0;
}
static int snd_hdspe_get_autosync_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hdspe* hdspe = snd_kcontrol_chip(kcontrol);
int i;
struct hdspe_status s;
hdspe->m.read_status(hdspe, &s);
for (i=0; it.autosync_count-1; i++) {
int ref = hdspe->t.autosync_idx2ref[i];
ucontrol->value.enumerated.item[i] = s.freq[ref];
}
return 0;
}
/* ---------------- Internal frequency ------------------ */
static int snd_hdspe_info_internal_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
/* skip the first ("No Lock") frequency option */
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts_freq)-1,
texts_freq + 1);
return 0;
}
static int hdspe_get_internal_freq_idx(struct hdspe* hdspe)
{
return hdspe_internal_freq(hdspe)-1;
}
static int hdspe_put_internal_freq_idx(struct hdspe* hdspe, int val)
{
int changed = 0;
int pitch = hdspe_internal_pitch(hdspe);
dev_dbg(hdspe->card->dev, "%s(%d): idx %d -> freq %d, pitch = %d\n",
__func__, val, val, val+1, pitch);
changed = hdspe_write_internal_freq(hdspe, val+1);
/* Preserve pitch (essentially ratio of effective (internal) sample rate
* over standard (internal) sample rate) */
if (hdspe_write_internal_pitch(hdspe, pitch)) {
HDSPE_CTL_NOTIFY(dds);
}
return changed;
}
HDSPE_RW_ENUM_METHODS(internal_freq,
hdspe_get_internal_freq_idx, hdspe_put_internal_freq_idx,
false);
/* ---------------------- MADI specific ------------------- */
HDSPE_RW_ENUM_REG_METHODS(madi_sswclk, control, madi, WCK48, false)
HDSPE_RW_ENUM_REG_METHODS(madi_LineOut, control, madi, LineOut, false)
HDSPE_RW_ENUM_REG_METHODS(madi_clr_tms, control, madi, CLR_TMS, false)
HDSPE_RW_ENUM_REG_METHODS(madi_tx_64ch, control, madi, tx_64ch, false)
HDSPE_RO_ENUM_REG_METHODS(madi_rx_64ch, status0, madi, rx_64ch)
#define snd_hdspe_info_madi_rx_64ch snd_ctl_boolean_mono_info
HDSPE_RW_ENUM_REG_METHODS(madi_smux, control, madi, SMUX, false)
HDSPE_RW_ENUM_REG_METHODS(madi_autoinput, control, madi, AutoInp, false)
static int snd_hdspe_info_madi_input_source(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[] = {
HDSPE_MADI_INPUT_NAME(0),
HDSPE_MADI_INPUT_NAME(1)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
#define snd_hdspe_info_madi_input_select snd_hdspe_info_madi_input_source
HDSPE_RW_ENUM_REG_METHODS(madi_input_select, control, madi, inp_0, false)
HDSPE_RO_ENUM_REG_METHODS(madi_input_source, status0, madi, AB_int)
static int snd_hdspe_info_external_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
ENUMERATED_CTL_INFO(uinfo, texts_freq);
return 0;
}
static enum hdspe_freq hdspe_get_external_freq(struct hdspe* hdspe)
{
return hdspe_madi_get_external_freq(hdspe);
}
HDSPE_RO_ENUM_METHODS(external_freq, hdspe_get_external_freq);
/* ------------------------ AES specific ------------------- */
HDSPE_RW_ENUM_REG_METHODS(aes_ds_mode, control, aes, ds_mode, false)
HDSPE_RW_ENUM_REG_METHODS(aes_qs_mode, control, aes, qs_mode, false)
HDSPE_RW_ENUM_REG_METHODS(aes_emp, control, aes, EMP, false)
HDSPE_RW_ENUM_REG_METHODS(aes_dolby, control, aes, Dolby, false)
HDSPE_RW_ENUM_REG_METHODS(aes_pro, control, aes, PRO, false)
HDSPE_RW_ENUM_REG_METHODS(aes_LineOut, control, aes, LineOut, false)
HDSPE_RW_ENUM_REG_METHODS(aes_sswclk, control, aes, WCK48, false)
HDSPE_RW_ENUM_REG_METHODS(aes_clr_tms, control, aes, CLR_TMS, false)
static int snd_hdspe_info_aes_ds_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[] = {
HDSPE_DS_MODE_NAME(0),
HDSPE_DS_MODE_NAME(1)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
static int snd_hdspe_info_aes_qs_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_QS_MODE_COUNT] = {
HDSPE_QS_MODE_NAME(0),
HDSPE_QS_MODE_NAME(1),
HDSPE_QS_MODE_NAME(2)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
/* ---------------------- RayDAT / AIO / AIO Pro ---------------- */
HDSPE_RW_ENUM_REG_METHODS(raio_spdif_opt, settings, raio, Spdif_Opt, false)
HDSPE_RW_ENUM_REG_METHODS(raio_spdif_pro, settings, raio, Pro, false)
HDSPE_RW_ENUM_REG_METHODS(raio_aeb1, settings, raio, AEB1, false)
HDSPE_RW_ENUM_REG_METHODS(raio_aeb2, settings, raio, AEB2, false)
HDSPE_RW_ENUM_REG_METHODS(raio_sswclk, settings, raio, Wck48, false)
HDSPE_RW_ENUM_REG_METHODS(raio_clr_tms, settings, raio, clr_tms, false)
HDSPE_RW_ENUM_REG_METHODS(aio_xlr, settings, raio, Sym6db, false)
/* --------------------- S/PDIF input ---------------------- */
static int snd_hdspe_info_raio_spdif_in(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_RAIO_SPDIF_INPUT_COUNT] = {
HDSPE_RAIO_SPDIF_INPUT_NAME(0),
HDSPE_RAIO_SPDIF_INPUT_NAME(1),
HDSPE_RAIO_SPDIF_INPUT_NAME(2)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
HDSPE_RW_ENUM_REG_METHODS(raio_spdif_in, settings, raio, Input, false)
/* --------------------- AIO AD / DA / Phones level ----------------- */
static int snd_hdspe_info_aio_input_level(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_AIO_LEVEL_COUNT] = {
"Lo Gain",
"+4 dBu",
"-10 dBV"
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
static int snd_hdspe_info_aio_out_level(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_AIO_LEVEL_COUNT] = {
"Hi Gain",
"+4 dBu",
"-10 dBV"
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
#define snd_hdspe_info_aio_output_level snd_hdspe_info_aio_out_level
#define snd_hdspe_info_aio_phones_level snd_hdspe_info_aio_out_level
HDSPE_RW_ENUM_REG_METHODS(aio_input_level, settings, raio, AD_GAIN, false)
HDSPE_RW_ENUM_REG_METHODS(aio_output_level, settings, raio, DA_GAIN, false)
HDSPE_RW_ENUM_REG_METHODS(aio_phones_level, settings, raio, PH_GAIN, false)
/* ------------------ AIO PRO AD / DA / Phones level ---------------*/
static int snd_hdspe_info_aio_pro_input_level(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_AIO_PRO_INPUT_LEVEL_COUNT] = {
HDSPE_AIO_PRO_INPUT_LEVEL_NAME(0),
HDSPE_AIO_PRO_INPUT_LEVEL_NAME(1),
HDSPE_AIO_PRO_INPUT_LEVEL_NAME(2),
HDSPE_AIO_PRO_INPUT_LEVEL_NAME(3)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
#define snd_hdspe_get_aio_pro_input_level snd_hdspe_get_aio_input_level
#define snd_hdspe_put_aio_pro_input_level snd_hdspe_put_aio_input_level
/* AIO Pro output level here combines DA_GAIN and Sym6db. The output
* voltage range is no longer a simple 6dB different whether Sym6db
* is on or off. */
static int snd_hdspe_info_aio_pro_output_level(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_AIO_PRO_OUTPUT_LEVEL_COUNT] = {
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(0),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(1),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(2),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(3),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(4),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(5),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(6),
HDSPE_AIO_PRO_OUTPUT_LEVEL_NAME(7)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
static int hdspe_get_aio_pro_output_level(struct hdspe* hdspe)
{
struct hdspe_settings_reg_raio settings = hdspe->reg.settings.raio;
return settings.DA_GAIN + (settings.Sym6db ? 4 : 0);
}
static int hdspe_put_aio_pro_output_level(struct hdspe* hdspe, int val)
{
struct hdspe_settings_reg_raio *settings = &hdspe->reg.settings.raio;
settings->DA_GAIN = val%4;
settings->Sym6db = val/4;
hdspe_write_settings(hdspe);
return 0;
}
HDSPE_RW_ENUM_METHODS(aio_pro_output_level,
hdspe_get_aio_pro_output_level,
hdspe_put_aio_pro_output_level, false)
static int snd_hdspe_info_aio_pro_phones_level(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[HDSPE_AIO_PRO_PHONES_LEVEL_COUNT] = {
HDSPE_AIO_PRO_PHONES_LEVEL_NAME(0),
HDSPE_AIO_PRO_PHONES_LEVEL_NAME(1)
};
ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
#define snd_hdspe_get_aio_pro_phones_level snd_hdspe_get_aio_phones_level
#define snd_hdspe_put_aio_pro_phones_level snd_hdspe_put_aio_phones_level
static int hdspe_get_aio_ao4s(struct hdspe* hdspe)
{
return !hdspe_read_status2(hdspe).raio.AEBO_D;
}
HDSPE_RO_ENUM_METHODS(aio_ao4s, hdspe_get_aio_ao4s)
static int hdspe_get_aio_ai4s(struct hdspe* hdspe)
{
return !hdspe_read_status2(hdspe).raio.AEBI_D;
}
HDSPE_RO_ENUM_METHODS(aio_ai4s, hdspe_get_aio_ai4s)
/* --------------------------------------------------------- */
/* Common control elements, except those for which we need to keep the id. The
* ones we need to keep the id for, are added explicitly in snd_hdspe_create_controls(). */
static const struct snd_kcontrol_new snd_hdspe_controls_common[] = {
HDSPE_RW_KCTL(CARD, "Clock Mode", clock_mode),
HDSPE_RW_KCTL(CARD, "Preferred AutoSync Reference", pref_sync_ref),
};
static const struct snd_kcontrol_new snd_hdspe_controls_madi[] = {
HDSPE_RW_BOOL_KCTL(CARD, "TX 64 Channels Mode", madi_tx_64ch),
HDSPE_RW_BOOL_KCTL(CARD, "Double Wire Mode", madi_smux),
HDSPE_RW_BOOL_KCTL(CARD, "Autoselect Input", madi_autoinput),
HDSPE_RW_KCTL(CARD, "Preferred Input", madi_input_select),
HDSPE_RW_BOOL_KCTL(CARD, "Line Out", madi_LineOut),
HDSPE_RW_BOOL_KCTL(CARD, "Single Speed WordClk Out", madi_sswclk),
HDSPE_RW_BOOL_KCTL(CARD, "Clear TMS", madi_clr_tms),
};
static const struct snd_kcontrol_new snd_hdspe_controls_madiface[] = {
HDSPE_RW_KCTL(CARD, "Clock Mode", clock_mode),
#ifdef OLDSTUFF
HDSPE_RV_KCTL(CARD, "Sample Rate", sample_rate),
HDSPE_RW_KCTL(CARD, "Internal Frequency", internal_freq),
HDSPE_RV_KCTL(CARD, "External Frequency", external_freq),
// TODO
HDSPE_SYNC_STATUS("MADI Status", HDSPE_CLOCK_SOURCE_MADI),
#endif /*OLDSTUFF*/
HDSPE_RW_BOOL_KCTL(CARD, "TX 64 Channels Mode", madi_tx_64ch),
HDSPE_RV_BOOL_KCTL(CARD, "RX 64 Channels Mode", madi_rx_64ch),
HDSPE_RW_BOOL_KCTL(CARD, "Safe Mode", madi_autoinput),
// HDSPE_RV_KCTL(CARD, "Speed Mode", speed_mode)
};
static const struct snd_kcontrol_new snd_hdspe_controls_aes[] = {
HDSPE_RW_KCTL(CARD, "Double Speed Mode", aes_ds_mode),
HDSPE_RW_KCTL(CARD, "Quad Speed Mode", aes_qs_mode),
HDSPE_RW_BOOL_KCTL(CARD, "Professional", aes_pro),
HDSPE_RW_BOOL_KCTL(CARD, "Emphasis", aes_emp),
HDSPE_RW_BOOL_KCTL(CARD, "Non Audio", aes_dolby),
HDSPE_RW_BOOL_KCTL(CARD, "Line Out", aes_LineOut),
HDSPE_RW_BOOL_KCTL(CARD, "Single Speed WordClk Out", aes_sswclk),
HDSPE_RW_BOOL_KCTL(CARD, "Clear TMS", aes_clr_tms),
};
static const struct snd_kcontrol_new snd_hdspe_controls_raydat[] = {
HDSPE_RW_KCTL(CARD, "S/PDIF In", raio_spdif_in),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Optical", raio_spdif_opt),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Professional", raio_spdif_pro),
HDSPE_RW_BOOL_KCTL(CARD, "ADAT1 Internal", raio_aeb1),
HDSPE_RW_BOOL_KCTL(CARD, "ADAT2 Internal", raio_aeb2),
HDSPE_RW_BOOL_KCTL(CARD, "Single Speed WordClk Out", raio_sswclk),
HDSPE_RW_BOOL_KCTL(CARD, "Clear TMS", raio_clr_tms),
};
static const struct snd_kcontrol_new snd_hdspe_controls_aio[] = {
HDSPE_RO_BOOL_KCTL(CARD, "AO4S Present", aio_ao4s),
HDSPE_RO_BOOL_KCTL(CARD, "AI4S Present", aio_ai4s),
HDSPE_RW_KCTL(CARD, "S/PDIF In", raio_spdif_in),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Optical", raio_spdif_opt),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Professional", raio_spdif_pro),
HDSPE_RW_BOOL_KCTL(CARD, "ADAT Internal", raio_aeb1),
HDSPE_RW_BOOL_KCTL(CARD, "Single Speed WordClk Out", raio_sswclk),
HDSPE_RW_BOOL_KCTL(CARD, "Clear TMS", raio_clr_tms),
HDSPE_RW_BOOL_KCTL(CARD, "XLR Breakout Cable", aio_xlr),
HDSPE_RW_KCTL(CARD, "Input Level", aio_input_level),
HDSPE_RW_KCTL(CARD, "Output Level", aio_output_level),
HDSPE_RW_KCTL(CARD, "Phones Level", aio_phones_level)
};
static const struct snd_kcontrol_new snd_hdspe_controls_aio_pro[] = {
HDSPE_RW_KCTL(CARD, "S/PDIF In", raio_spdif_in),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Optical", raio_spdif_opt),
HDSPE_RW_BOOL_KCTL(CARD, "S/PDIF Out Professional", raio_spdif_pro),
HDSPE_RW_BOOL_KCTL(CARD, "ADAT Internal", raio_aeb1),
HDSPE_RW_BOOL_KCTL(CARD, "Single Speed WordClk Out", raio_sswclk),
HDSPE_RW_BOOL_KCTL(CARD, "Clear TMS", raio_clr_tms),
HDSPE_RW_KCTL(CARD, "Input Level", aio_pro_input_level),
HDSPE_RW_KCTL(CARD, "Output Level", aio_pro_output_level),
HDSPE_RW_KCTL(CARD, "Phones Level", aio_pro_phones_level),
};
HDSPE_RO_INT1_HDSPE_METHODS(firmware_rev, 0, 0, 1)
HDSPE_RO_INT1_HDSPE_METHODS(fw_build, 0, 0, 1)
HDSPE_RO_INT1_HDSPE_METHODS(serial, 0, 0, 1)
#define snd_hdspe_info_running snd_ctl_boolean_mono_info
HDSPE_RO_ENUM_HDSPE_METHODS(running)
HDSPE_RO_INT1_HDSPE_METHODS(capture_pid, 0, 0, 1)
HDSPE_RO_INT1_HDSPE_METHODS(playback_pid, 0, 0, 1)
HDSPE_RO_INT1_METHODS(buffer_size, 32, 8192, 1, hdspe_period_size)
static int hdspe_is_tco_present(struct hdspe* hdspe)
{
return hdspe->tco != NULL;
}
#define snd_hdspe_info_tco_present snd_ctl_boolean_mono_info
HDSPE_RO_ENUM_METHODS(tco_present, hdspe_is_tco_present)
static const struct snd_kcontrol_new snd_hdspe_controls_cardinfo[] = {
HDSPE_RO_KCTL(CARD, "Card Revision", firmware_rev),
HDSPE_RO_KCTL(CARD, "Firmware Build", fw_build),
HDSPE_RO_KCTL(CARD, "Serial", serial),
HDSPE_RO_KCTL(CARD, "TCO Present", tco_present),
HDSPE_RV_KCTL(CARD, "Capture PID", capture_pid),
HDSPE_RV_KCTL(CARD, "Playback PID", playback_pid),
};
struct snd_kcontrol* hdspe_add_control(struct hdspe* hdspe,
const struct snd_kcontrol_new* newctl)
{
struct snd_kcontrol* ctl = snd_ctl_new1(newctl, hdspe);
int err = snd_ctl_add(hdspe->card, ctl);
return (err < 0) ? ERR_PTR(err) : ctl;
}
int hdspe_add_controls(struct hdspe *hdspe,
int count, const struct snd_kcontrol_new *list)
{
int i;
for (i = 0; i < count; i++) {
int err = snd_ctl_add(hdspe->card,
snd_ctl_new1(&list[i], hdspe));
if (err < 0)
return err;
}
return 0;
}
int hdspe_add_control_id(struct hdspe* hdspe,
const struct snd_kcontrol_new* nctl,
struct snd_ctl_elem_id** ctl_id)
{
struct snd_kcontrol* ctl = hdspe_add_control(hdspe, nctl);
if (IS_ERR(ctl))
return -PTR_ERR(ctl);
*ctl_id = &ctl->id;
return 0;
}
int snd_hdspe_create_controls(struct snd_card *card,
struct hdspe *hdspe)
{
unsigned int limit;
int err;
const struct snd_kcontrol_new *list = NULL;
/* Card info controls */
err = hdspe_add_controls(hdspe,
ARRAY_SIZE(snd_hdspe_controls_cardinfo),
snd_hdspe_controls_cardinfo);
if (err < 0)
return err;
HDSPE_ADD_RV_CONTROL_ID(CARD, "Running", running);
HDSPE_ADD_RV_CONTROL_ID(CARD, "Buffer Size", buffer_size);
HDSPE_ADD_RWV_CONTROL_ID(CARD, "Status Polling", status_polling);
HDSPE_ADD_RV_CONTROL_ID(HWDEP, "Raw Sample Rate", raw_sample_rate);
HDSPE_ADD_RW_CONTROL_ID(HWDEP, "DDS", dds);
HDSPE_ADD_RW_CONTROL_ID(CARD, "Internal Frequency", internal_freq);
/* Common controls: sample rate etc ... */
if (hdspe->io_type != HDSPE_MADIFACE) {
HDSPE_ADD_RV_CONTROL_ID(CARD, "Current AutoSync Reference", autosync_ref);
err = hdspe_add_controls(hdspe,
ARRAY_SIZE(snd_hdspe_controls_common),
snd_hdspe_controls_common);
if (err < 0)
return err;
}
/* AutoSync status and frequency */
if (hdspe->io_type != HDSPE_MADIFACE) {
HDSPE_ADD_RV_CONTROL_ID(CARD, "AutoSync Status",
autosync_status);
HDSPE_ADD_RV_CONTROL_ID(CARD, "AutoSync Frequency",
autosync_freq);
}
/* Card specific controls */
switch (hdspe->io_type) {
case HDSPE_MADI:
HDSPE_ADD_RV_CONTROL_ID(CARD, "External Frequency", external_freq);
HDSPE_ADD_RV_CONTROL_ID(CARD, "Current Input", madi_input_source);
HDSPE_ADD_RV_CONTROL_ID(CARD, "RX 64 Channels Mode", madi_rx_64ch);
list = snd_hdspe_controls_madi;
limit = ARRAY_SIZE(snd_hdspe_controls_madi);
break;
case HDSPE_MADIFACE:
list = snd_hdspe_controls_madiface;
limit = ARRAY_SIZE(snd_hdspe_controls_madiface);
break;
case HDSPE_AES:
list = snd_hdspe_controls_aes;
limit = ARRAY_SIZE(snd_hdspe_controls_aes);
break;
case HDSPE_RAYDAT:
list = snd_hdspe_controls_raydat;
limit = ARRAY_SIZE(snd_hdspe_controls_raydat);
break;
case HDSPE_AIO:
list = snd_hdspe_controls_aio;
limit = ARRAY_SIZE(snd_hdspe_controls_aio);
break;
case HDSPE_AIO_PRO:
list = snd_hdspe_controls_aio_pro;
limit = ARRAY_SIZE(snd_hdspe_controls_aio_pro);
break;
default:
snd_BUG();
}
err = hdspe_add_controls(hdspe, limit, list);
if (err < 0)
return err;
/* Mixer controls, in hdspe_mixer.c */
err = hdspe_create_mixer_controls(hdspe);
if (err < 0)
return err;
/* TCO controls, in hdspe_tco.c */
if (hdspe->tco) {
err = hdspe_create_tco_controls(hdspe);
if (err < 0)
return err;
}
return 0;
}
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_control.h 0000664 0000000 0000000 00000037427 15060370513 0022374 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* @file hdspe-control.h
* @brief RME HDSPe sound card driver status and control interface helpers.
*
* 20210728,0907,08,09,10,20220330 - Philippe.Bekaert@uhasselt.be
*
* Based on earlier work of the other MODULE_AUTHORs,
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
#ifndef _HDSPE_CONTROL_H_
#define _HDSPE_CONTROL_H_
// TODO: when to use locking / use_is_eclusive() ?
// TODO: put() argument range checking needed?
// TODO: put() return value? always 0, or 1 when changed?
#include
/**
* ENUMERATE_CTL_INFO - enumerated snd_kcontrol_new struct info method
* helper.
*/
#define ENUMERATED_CTL_INFO(info, texts) \
snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
/**
* HDSPE_RO_KCTL - generate a snd_kcontrol_new struct for a read-only
* non-volatile property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RO_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ, \
.info = snd_hdspe_info_##prop, \
.get = snd_hdspe_get_##prop \
}
/**
* HDSPE_RV_KCTL - generate a snd_kcontrol_new struct for a read-only
* volatile property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RV_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspe_info_##prop, \
.get = snd_hdspe_get_##prop \
}
/**
* HDSPE_RW_KCTL - generate a snd_kcontrol_new struct for a read-write property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RW_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.info = snd_hdspe_info_##prop, \
.get = snd_hdspe_get_##prop, \
.put = snd_hdspe_put_##prop \
}
/**
* HDSPE_RWV_KCTL - generate a snd_kcontrol_new struct for a read-write
* volatile property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RWV_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_hdspe_info_##prop, \
.get = snd_hdspe_get_##prop, \
.put = snd_hdspe_put_##prop \
}
/**
* HDSPE_WO_KCTL - generate a snd_kcontrol_new struct for a write-only property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_WO_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_WRITE, \
.info = snd_hdspe_info_##prop, \
.put = snd_hdspe_put_##prop \
}
/**
* HDSPE_RO_BOOL_KCTL - generate a snd_kcontrol_new struct for a
* boolean read-only non-volatile property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RO_BOOL_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ, \
.info = snd_ctl_boolean_mono_info, \
.get = snd_hdspe_get_##prop \
}
/**
* HDSPE_RV_BOOL_KCTL - generate a snd_kcontrol_new struct for a
* boolean read-only volatile property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RV_BOOL_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.info = snd_ctl_boolean_mono_info, \
.get = snd_hdspe_get_##prop \
}
/**
* HDSPE_RW_BOOL_KCTL - generate a snd_kcontrol_new struct for a
* boolean read-write property.
* @xface: MIXER, CARD, etc...
* @xname: display name for the property.
* @prop: source code name for the property.
*/
#define HDSPE_RW_BOOL_KCTL(xface, xname, prop) \
{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \
.name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.info = snd_ctl_boolean_mono_info, \
.get = snd_hdspe_get_##prop, \
.put = snd_hdspe_put_##prop \
}
/**
* Get the current value for a property
* @kcontrol: control element.
* @get: getter function - does the real work.
* @lock_req: protect get() by spinlock, if true.
* @propname: name of the property, for debugging
* Returns what get() returns.
*/
extern int hdspe_control_get(struct snd_kcontrol *kcontrol,
int (*get)(struct hdspe*),
bool lock_req, const char* propname);
/**
* Set a property to a new value.
* @kcontrol: control element.
* @val: value to set.
* @get: getter function - gets current value, for change detection.
* If NULL, change detection is left to put().
* @put: putter function - does the real work of setting the new value.
* @lock_req: protect get() and put() by spinlock, if true.
* @excl_req: return -EBUSY if we have no exclusive access, if true.
* @propname: name of the property, for debugging
* Returns the return code of put() if nonzero. If put() returns zero,
* returns 1 if the property changed and 0 otherwise.
*/
extern int hdspe_control_put(struct snd_kcontrol *kcontrol, int val,
int (*get)(struct hdspe*),
int (*put)(struct hdspe*, int val),
bool lock_req, bool excl_req, const char* propnam);
/**
* HDSPE_GETTER: create code for a hdspe_get_(struct hdspe* hdspe)
* getter function, simply returns hdspe->.
* @prop: property name, same as struct hdspe int member name.
*/
#define HDSPE_GETTER(prop) \
static int hdspe_get_##prop(struct hdspe* hdspe) \
{ \
return hdspe->prop; \
}
/**
* HDSPE_PUTTER: create code for a
* hdspe_put_(struct hdspe* hdspe, int val)
* putter function, simply setting hdspe-> and returning 1 if
* its value changed and 0 if not.
* @prop: property name, same as struct hdspe int member name.
*/
#define HDSPE_PUTTER(prop) \
static int hdspe_put_##prop(struct hdspe* hdspe, int val) \
{ \
int changed = val != hdspe->prop; \
hdspe->prop = val; \
return changed; \
}
/**
* HDSPE_REG_GETTER helpers.
*/
static inline __attribute__((always_inline))
union hdspe_control_reg hdspe_read_control(struct hdspe* hdspe)
{
return hdspe->reg.control;
}
static inline __attribute__((always_inline))
union hdspe_settings_reg hdspe_read_settings(struct hdspe* hdspe)
{
return hdspe->reg.settings;
}
/**
* HDSPE_REG_GETTER: creates code for a hdspe_get_(struct hdspe* hdspe)
* getter function, simply reading hdspe->reg....
* @prop: property name (defines generated function name).
* @regname: control, settings, or status0: a field of hdspe->reg.
* @model: madi, aes or raio: a field in hdspe->reg.regname.
* @field: a bitfield in hdspe->reg.regname.model.
* @do_read: if true, read the register from hardware (for status0 register).
*/
#define HDSPE_REG_GETTER(prop, regname, model, field, do_read) \
static int hdspe_get_##prop(struct hdspe* hdspe) \
{ \
if (do_read) hdspe_read_##regname(hdspe); \
return hdspe->reg.regname.model.field; \
}
/**
* HDSPE_REG_PUTTER: creates code for a
* hdspe_put_(struct hdspe* hdspe,int val) putter
* function, writing hdspe->reg.... if
* it is not changing.
* @prop: property name (defines generated function name).
* @regname: control or settings: a field of hdspe->reg. (status registers
* do not need putters).
* @model: madi, aes or raio: a field in hdspe->reg.regname.
* @field: a bitfield in hdspe->reg.regname.model.
* Returns 1 if the value has changed and 0 if not.
*/
#define HDSPE_REG_PUTTER(prop, regname, model, field) \
static int hdspe_put_##prop(struct hdspe* hdspe, int val) \
{ \
int oldval = hdspe->reg.regname.model.field; \
if (val != oldval) { \
hdspe->reg.regname.model.field = val; \
hdspe_write_##regname(hdspe); \
return 1; \
} \
return 0; \
}
/**
* HDSPE_ENUM_GET_REG - generate code for a snd_kcontrol_new .get method
* named snd_hdspe_get_, essentially just reading a bitfield from a
* register.
* @prop: property name (defines generated function name).
* @regname: control, settings, or status0: a field of hdspe->reg.
* @model: madi, aes or raio: a field in hdspe->reg.regname.
* @field: a bitfield in hdspe->reg.regname.model.
* @lock_req: if nonzero, protect reading by spin_lock_irq(&hdspe->lock).
* @do_read: if true, read the register from hardware (for status0 register).
* The generated snd_hdspe_get_ method simply gets the indicated
* bitfield from the register.
*/
#define HDSPE_ENUM_GET_REG(prop, regname, model, field, lock_req, do_read) \
HDSPE_REG_GETTER(prop, regname, model, field, do_read) \
static int snd_hdspe_get_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
ucontrol->value.enumerated.item[0] = \
hdspe_control_get(kcontrol, hdspe_get_##prop, lock_req, #prop); \
return 0; \
}
/**
* Same, but using a generic int (*getter)(struct hdspe* hdspe).
*/
#define HDSPE_ENUM_GET(prop, getter, lock_req) \
static int snd_hdspe_get_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol); \
ucontrol->value.enumerated.item[0] = getter(hdspe); \
return 0; \
}
/**
* HDSPE_ENUM_PUT_REG - generate code for a snd_kcontrol_new .put method
* names snd_hdspe_put_, essentially just writing a bitfield in a
* register.
* @regname: control, settings or status0, one of the fields of
* hdspe->reg.
* @model: madi, aes or raio, a field in hdspe->reg.regname.
* @field: a bitfield in hdspe->reg.regname.card.
* @lock_req: if nonzero, protect writing by spin_lock_irq(&hdspe->lock).
* @excl_req: if nonzero, return -EBUSY immediately if we have no
* exclusive control over the card.
* The generated snd_hdspe_put_ method sets the indicated
* bitfield in the register and writes the register to hardware if its
* value has changed.
* The method returns 1 if the value is changed and 0 if not.
*/
#define HDSPE_ENUM_PUT_REG(prop, regname, model, field, lock_req, excl_req) \
HDSPE_REG_PUTTER(prop, regname, model, field) \
static int snd_hdspe_put_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
return hdspe_control_put( \
kcontrol, ucontrol->value.enumerated.item[0], \
NULL, hdspe_put_##prop, lock_req, excl_req, #prop); \
}
/**
* Same, but using a generic int (*getter)(struct hdspe* hdspe) and
* void (*putter)(struct hdspe* hdspe, int val).
*/
#define HDSPE_ENUM_PUT(prop, getter, putter, lock_req, excl_req) \
static int snd_hdspe_put_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
return hdspe_control_put( \
kcontrol, ucontrol->value.enumerated.item[0], \
getter, putter, lock_req, excl_req, #prop); \
}
/**
* HDSPE_RW_ENUM_REG_METHODS - generates a snd_kcontrol_new .get and .put
* method for read-write control elements one-to-one corresponding to a bit
* field in the control or settings register.
*/
#define HDSPE_RW_ENUM_REG_METHODS(prop, regname, card, field, excl_req) \
HDSPE_ENUM_GET_REG(prop, regname, card, field, true, false) \
HDSPE_ENUM_PUT_REG(prop, regname, card, field, true, excl_req)
/**
* Same, using a generic getter and putter.
*/
#define HDSPE_RW_ENUM_METHODS(prop, getter, putter, excl_req) \
HDSPE_ENUM_GET(prop, getter, true) \
HDSPE_ENUM_PUT(prop, getter, putter, true, excl_req)
/**
* HDSPE_RO_ENUM_REG_METHODS - generates a snd_kcontrol_new .get method
* for read-only control elements one-to-one corresponding to a bit
* field in a status register.
*/
#define HDSPE_RO_ENUM_REG_METHODS(prop, regname, card, field) \
HDSPE_ENUM_GET_REG(prop, regname, card, field, false, true)
/**
* Same, using a generic getter.
*/
#define HDSPE_RO_ENUM_METHODS(prop, getter) \
HDSPE_ENUM_GET(prop, getter, false)
/**
* Same, for single channel integer properties instead of enum.
*/
#define HDSPE_INT1_INFO(prop, minval, maxval, stepval) \
static int snd_hdspe_info_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_info *uinfo) \
{ \
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; \
uinfo->count = 1; \
uinfo->value.integer.min = minval; \
uinfo->value.integer.max = maxval; \
uinfo->value.integer.step = stepval; \
return 0; \
}
#define HDSPE_INT1_GET(prop, getter, lock_req) \
static int snd_hdspe_get_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
struct hdspe *hdspe = snd_kcontrol_chip(kcontrol); \
ucontrol->value.integer.value[0] = getter(hdspe); \
return 0; \
}
#define HDSPE_INT1_PUT(prop, getter, putter, lock_req, excl_req) \
static int snd_hdspe_put_##prop(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
return hdspe_control_put( \
kcontrol, ucontrol->value.integer.value[0], \
getter, putter, lock_req, excl_req, #prop); \
}
#define HDSPE_RO_INT1_METHODS(prop, min, max, step, getter) \
HDSPE_INT1_INFO(prop, min, max, step) \
HDSPE_INT1_GET(prop, getter, false)
#define HDSPE_RW_INT1_METHODS(prop, min, max, step, getter, putter, excl_req) \
HDSPE_INT1_INFO(prop, min, max, step) \
HDSPE_INT1_GET(prop, getter, true) \
HDSPE_INT1_PUT(prop, getter, putter, true, excl_req)
/**
* HDSPE_RW_INT1_HDSPE_METHODS: create methods for reading/writing
* the integer struct hdspe member .
*/
#define HDSPE_RW_INT1_HDSPE_METHODS(prop, min, max, step) \
HDSPE_GETTER(prop) \
HDSPE_PUTTER(prop) \
HDSPE_RW_INT1_METHODS(prop, min, max, step, \
hdspe_get_##prop, hdspe_put_##prop, false)
#define HDSPE_RO_INT1_HDSPE_METHODS(prop, min, max, step) \
HDSPE_GETTER(prop) \
HDSPE_RO_INT1_METHODS(prop, min, max, step, hdspe_get_##prop)
#define HDSPE_RO_ENUM_HDSPE_METHODS(prop) \
HDSPE_GETTER(prop) \
HDSPE_RO_ENUM_METHODS(prop, hdspe_get_##prop)
/**
* HDSPE_ADD_CONTROL_ID: generates code for adding a control element,
* storing the created snd_ctl_elem_id* in hdspe->cid., for
* sending notifications later on.
* @nctldecl: struct snd_kcontol_new initializer (e.g. HDSPE_RO_KCTL(...))
* @prop: name of the property, at the same time name of a member
* of hdspe->cid.
*/
#define HDSPE_ADD_CONTROL_ID(nctldecl, prop) \
{ \
const struct snd_kcontrol_new nctl = nctldecl; \
int err = hdspe_add_control_id(hdspe, &nctl, &hdspe->cid.prop); \
if (err < 0) \
return err; \
}
#define HDSPE_ADD_RO_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RO_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RV_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RV_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RO_BOOL_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RO_BOOL_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RV_BOOL_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RV_BOOL_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RW_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RW_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RW_BOOL_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RW_BOOL_KCTL(iface, name, prop), prop)
#define HDSPE_ADD_RWV_CONTROL_ID(iface, name, prop) \
HDSPE_ADD_CONTROL_ID(HDSPE_RWV_KCTL(iface, name, prop), prop)
#endif /* _HDSPE_CONTROL_H_ */
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_core.c 0000664 0000000 0000000 00000041301 15060370513 0021621 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for RME HDSPe MADI/AES/RayDAT/AIO/AIO Pro audio interface(s)
*
* Copyright (c) 2003 Winfried Ritsch (IEM)
* code based on hdsp.c Paul Davis
* Marcus Andersson
* Thomas Charbonnel
* Modified 2006-06-01 for AES support by Remy Bruno
*
*
* Modified 2009-04-13 for proper metering by Florian Faber
*
*
* Modified 2009-04-14 for native float support by Florian Faber
*
*
* Modified 2009-04-26 fixed bug in rms metering by Florian Faber
*
*
* Modified 2009-04-30 added hw serial number support by Florian Faber
*
* Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
*
* Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
*
* Modified 2019-05-23 fix AIO single speed ADAT capture and playback
* by Philippe.Bekaert@uhasselt.be
*
* Modified 2021-07 ... 2021-12 AIO Pro support, fixes, register
* documentation, clean up, refactoring, updated user space API,
* renamed hdspe, updated control elements, TCO LTC output, double/quad
* speed AIO / AIO Pro fixes, ...
* by Philippe.Bekaert@uhasselt.be
*/
#include "hdspe.h"
#include "hdspe_core.h"
#include
#include
#include
#include
#include
#include
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for RME HDSPE interface.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for RME HDSPE interface.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable/disable specific HDSPE soundcards.");
MODULE_AUTHOR
(
"Winfried Ritsch , "
"Paul Davis , "
"Marcus Andersson, Thomas Charbonnel , "
"Remy Bruno , "
"Florian Faber , "
"Adrian Knoth , "
"Philippe Bekaert "
);
MODULE_DESCRIPTION("RME HDSPe");
MODULE_LICENSE("GPL");
// This driver can obsolete old snd-hdspm driver.
MODULE_ALIAS("snd-hdspm");
/* RME PCI vendor ID as it is reported by the RME AIO PRO card */
#ifndef PCI_VENDOR_ID_RME
#define PCI_VENDOR_ID_RME 0x1d18
#endif /*PCI_VENDOR_ID_RME*/
static const struct pci_device_id snd_hdspe_ids[] = {
{.vendor = PCI_VENDOR_ID_XILINX,
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = 0,
.class_mask = 0,
.driver_data = 0},
{.vendor = PCI_VENDOR_ID_RME,
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = 0,
.class_mask = 0,
.driver_data = 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, snd_hdspe_ids);
/* interrupt handler */
static irqreturn_t snd_hdspe_interrupt(int irq, void *dev_id)
{
struct hdspe *hdspe = (struct hdspe *) dev_id;
int i, audio, midi, schedule = 0;
hdspe->reg.status0 = hdspe_read_status0_nocache(hdspe);
audio = hdspe->reg.status0.common.IRQ;
midi = hdspe->reg.status0.raw & hdspe->midiIRQPendingMask;
#ifdef TIME_INTERRUPT_INTERVAL
u64 now = ktime_get_raw_fast_ns();
dev_dbg(hdspe->card->dev, "snd_hdspe_interrupt %llu us LAT=%d BUF_ID=%u BUF_PTR=%05u %s%s%s%s%s\n",
(now - hdspe->last_interrupt_time) / 1000,
hdspe->reg.control.common.LAT,
hdspe->reg.status0.common.BUF_ID,
le16_to_cpu(hdspe->reg.status0.common.BUF_PTR)<<6,
audio ? "AUDIO " : "",
hdspe->midiPorts>0 && (hdspe->reg.status0.raw & hdspe->midi[0].irq) ? "MIDI1 " : "",
hdspe->midiPorts>1 && (hdspe->reg.status0.raw & hdspe->midi[1].irq) ? "MIDI2 " : "",
hdspe->midiPorts>2 && (hdspe->reg.status0.raw & hdspe->midi[2].irq) ? "MIDI3 " : "",
hdspe->midiPorts>3 && (hdspe->reg.status0.raw & hdspe->midi[3].irq) ? "MIDI4 " : ""
);
hdspe->last_interrupt_time = now;
#endif /*TIME_INTERRUPT_INTERVAL*/
if (!audio && !midi)
return IRQ_NONE;
if (audio) {
hdspe_write(hdspe, HDSPE_interruptConfirmation, 0);
hdspe->irq_count++;
hdspe_update_frame_count(hdspe);
if (hdspe->tco) {
/* LTC In update must happen before user
* space is notified of a new period */
hdspe_tco_period_elapsed(hdspe);
}
if (hdspe->capture_substream)
snd_pcm_period_elapsed(hdspe->capture_substream);
if (hdspe->playback_substream)
snd_pcm_period_elapsed(hdspe->playback_substream);
/* status polling at user controlled rate */
if (hdspe->status_polling > 0 &&
jiffies >= hdspe->last_status_jiffies
+ HZ/hdspe->status_polling) {
hdspe->last_status_jiffies = jiffies;
schedule_work(&hdspe->status_work);
}
}
if (midi) {
schedule = 0;
for (i = 0; i < hdspe->midiPorts; i++) {
if ((hdspe_read(hdspe,
hdspe->midi[i].statusIn) & 0xff) &&
(hdspe->reg.status0.raw & hdspe->midi[i].irq)) {
/* we disable interrupts for this input until
* processing is done */
hdspe->reg.control.raw &= ~hdspe->midi[i].ie;
hdspe->midi[i].pending = 1;
schedule = 1;
}
}
if (schedule) {
hdspe_write_control(hdspe);
queue_work(system_highpri_wq, &hdspe->midi_work);
}
}
return IRQ_HANDLED;
}
/* Start audio and TCO MTC interrupts. Other MIDI interrupts
* are enabled when the MIDI devices are created. */
static void hdspe_start_interrupts(struct hdspe* hdspe)
{
if (hdspe->tco) {
/* TCO MTC port is always the last one */
struct hdspe_midi *m = &hdspe->midi[hdspe->midiPorts-1];
dev_dbg(hdspe->card->dev,
"%s: enabling TCO MTC input port %d '%s'.\n",
__func__, m->id, m->portname);
hdspe->reg.control.raw |= m->ie;
}
hdspe->reg.control.common.START =
hdspe->reg.control.common.IE_AUDIO = true;
hdspe_write_control(hdspe);
}
static void hdspe_stop_interrupts(struct hdspe* hdspe)
{
/* stop the audio, and cancel all interrupts */
hdspe->reg.control.common.START =
hdspe->reg.control.common.IE_AUDIO = false;
hdspe->reg.control.raw &= ~hdspe->midiInterruptEnableMask;
hdspe_write_control(hdspe);
}
/* Create ALSA devices, after hardware initialization */
static int snd_hdspe_create_alsa_devices(struct snd_card *card,
struct hdspe *hdspe)
{
int err, i;
dev_dbg(card->dev, "Create ALSA PCM devices ...\n");
err = snd_hdspe_create_pcm(card, hdspe);
if (err < 0)
return err;
dev_dbg(card->dev, "Create ALSA MIDI devices ...\n");
for (i = 0; i < hdspe->midiPorts; i++) {
err = snd_hdspe_create_midi(card, hdspe, i);
if (err < 0)
return err;
}
dev_dbg(card->dev, "Create ALSA hwdep ...\n");
err = snd_hdspe_create_hwdep(card, hdspe);
if (err < 0)
return err;
dev_dbg(card->dev, "Create ALSA controls ...\n");
err = snd_hdspe_create_controls(card, hdspe);
if (err < 0)
return err;
dev_dbg(card->dev, "Init proc interface...\n");
snd_hdspe_proc_init(hdspe);
dev_dbg(card->dev, "Initializing complete?\n");
err = snd_card_register(card);
if (err < 0) {
dev_err(card->dev, "error registering card.\n");
return err;
}
dev_dbg(card->dev, "... yes now\n");
return 0;
}
/* Initialize struct hdspe fields beyond PCI info, hardware vars, firmware
* revision and build, serial no, io_type, mixer and TCO. */
static int hdspe_init(struct hdspe* hdspe)
{
hdspe->pcm = NULL;
hdspe->hwdep = NULL;
hdspe->capture_substream = hdspe->playback_substream = NULL;
hdspe->capture_buffer = hdspe->playback_buffer = NULL;
hdspe->capture_pid = hdspe->playback_pid = -1;
hdspe->running = false;
hdspe->irq_count = 0;
// Initialize hardware registers and their cache, card_name, methods,
// and tables.
hdspe->reg.control.raw = hdspe->reg.settings.raw =
hdspe->reg.pll_freq = hdspe->reg.status0.raw = 0;
hdspe->reg.control.common.LAT = 6;
hdspe->reg.control.common.freq = HDSPE_FREQ_44_1KHZ;
hdspe->reg.control.common.LineOut = true;
hdspe_write_control(hdspe);
switch (hdspe->io_type) {
case HDSPE_MADI :
case HDSPE_MADIFACE: hdspe_init_madi(hdspe); break;
case HDSPE_AES : hdspe_init_aes(hdspe); break;
case HDSPE_RAYDAT :
case HDSPE_AIO :
case HDSPE_AIO_PRO : hdspe_init_raio(hdspe); break;
default : snd_BUG();
}
hdspe_read_status0_nocache(hdspe); // init reg.status0
hdspe_write_internal_pitch(hdspe, 1000000); // init reg.pll_freq
// Set the channel map according the initial speed mode */
hdspe_set_channel_map(hdspe, hdspe_speed_mode(hdspe));
return 0;
}
static void hdspe_terminate(struct hdspe* hdspe)
{
switch (hdspe->io_type) {
case HDSPE_MADI :
case HDSPE_MADIFACE: hdspe_terminate_madi(hdspe); break;
case HDSPE_AES : hdspe_terminate_aes(hdspe); break;
case HDSPE_RAYDAT :
case HDSPE_AIO :
case HDSPE_AIO_PRO : hdspe_terminate_raio(hdspe); break;
default : snd_BUG();
}
}
/* get card serial number - for older cards */
static uint32_t snd_hdspe_get_serial_rev1(struct hdspe* hdspe)
{
uint32_t serial = 0;
if (hdspe->io_type == HDSPE_MADIFACE)
return 0;
serial = (hdspe_read(hdspe, HDSPE_midiStatusIn0)>>8) & 0xFFFFFF;
/* id contains either a user-provided value or the default
* NULL. If it's the default, we're safe to
* fill card->id with the serial number.
*
* If the serial number is 0xFFFFFF, then we're dealing with
* an old PCI revision that comes without a sane number. In
* this case, we don't set card->id to avoid collisions
* when running with multiple cards.
*/
if (id[hdspe->dev] || serial == 0xFFFFFF) {
serial = 0;
}
return serial;
}
/* get card serial number - for newer cards */
static uint32_t snd_hdspe_get_serial_rev2(struct hdspe* hdspe)
{
uint32_t serial = 0;
// TODO: test endianness issues
/* get the serial number from the RD_BARCODE{0,1} registers */
int i;
union {
__le32 dw[2];
char c[8];
} barcode;
barcode.dw[0] = hdspe_read(hdspe, HDSPE_RD_BARCODE0);
barcode.dw[1] = hdspe_read(hdspe, HDSPE_RD_BARCODE1);
for (i = 0; i < 8; i++) {
int c = barcode.c[i];
if (c >= '0' && c <= '9')
serial = serial * 10 + (c - '0');
}
return serial;
}
/* Get card model. TODO: check against Mac and windows driver */
static enum hdspe_io_type hdspe_get_io_type(int pci_vendor_id, int firmware_rev)
{
switch (firmware_rev) {
case HDSPE_RAYDAT_REV:
return HDSPE_RAYDAT;
case HDSPE_AIO_REV:
return (pci_vendor_id == PCI_VENDOR_ID_RME) ?
HDSPE_AIO_PRO : HDSPE_AIO;
case HDSPE_MADIFACE_REV:
return HDSPE_MADIFACE;
default:
if ((firmware_rev == 0xf0) ||
((firmware_rev >= 0xe6) &&
(firmware_rev <= 0xea))) {
return HDSPE_AES;
} else if ((firmware_rev == 0xd2) ||
((firmware_rev >= 0xc8) &&
(firmware_rev <= 0xcf))) {
return HDSPE_MADI;
}
}
return HDSPE_IO_TYPE_INVALID;
}
static int snd_hdspe_create(struct hdspe *hdspe)
{
struct snd_card *card = hdspe->card;
struct pci_dev *pci = hdspe->pci;
int err;
unsigned long io_extent;
hdspe->irq = -1;
hdspe->port = 0;
hdspe->iobase = NULL;
spin_lock_init(&hdspe->lock);
INIT_WORK(&hdspe->midi_work, hdspe_midi_work);
INIT_WORK(&hdspe->status_work, hdspe_status_work);
pci_read_config_word(hdspe->pci,
PCI_CLASS_REVISION, &hdspe->firmware_rev);
hdspe->vendor_id = pci->vendor;
dev_dbg(card->dev,
"PCI vendor %04x, device %04x, class revision %x\n",
pci->vendor, pci->device, hdspe->firmware_rev);
strcpy(card->mixername, "RME HDSPe");
strcpy(card->driver, "HDSPe");
/* Determine card model */
hdspe->io_type = hdspe_get_io_type(hdspe->vendor_id,
hdspe->firmware_rev);
if (hdspe->io_type == HDSPE_IO_TYPE_INVALID) {
dev_err(card->dev,
"unknown firmware revision %d (0x%x)\n",
hdspe->firmware_rev, hdspe->firmware_rev);
return -ENODEV;
}
/* PCI */
err = pci_enable_device(pci);
if (err < 0)
return err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
#else
err = pci_set_dma_mask(pci, DMA_BIT_MASK(32));
#endif
if (!err) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
err = dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32));
#else
err = pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
#endif
}
if (err != 0) {
dev_err(card->dev, "No suitable DMA addressing support.\n");
return -ENODEV;
}
pci_set_master(hdspe->pci);
/* TODO: mac driver sets PCI latency timer to 255 ??? */
err = pci_request_regions(pci, "hdspe");
if (err < 0)
return err;
hdspe->port = pci_resource_start(pci, 0);
io_extent = pci_resource_len(pci, 0);
dev_dbg(card->dev, "grabbed memory region 0x%lx-0x%lx\n",
hdspe->port, hdspe->port + io_extent - 1);
hdspe->iobase = ioremap(hdspe->port, io_extent);
if (!hdspe->iobase) {
dev_err(card->dev, "unable to remap region 0x%lx-0x%lx\n",
hdspe->port, hdspe->port + io_extent - 1);
return -EBUSY;
}
dev_dbg(card->dev, "remapped region (0x%lx) 0x%lx-0x%lx\n",
(unsigned long)hdspe->iobase, hdspe->port,
hdspe->port + io_extent - 1);
if (request_irq(pci->irq, snd_hdspe_interrupt,
IRQF_SHARED, KBUILD_MODNAME, hdspe)) {
dev_err(card->dev, "unable to use IRQ %d\n", pci->irq);
return -EBUSY;
}
dev_dbg(card->dev, "use IRQ %d\n", pci->irq);
hdspe->irq = pci->irq;
card->sync_irq = hdspe->irq;
/* Firmware build */
hdspe->fw_build = le32_to_cpu(hdspe_read(hdspe, HDSPE_RD_FLASH)) >> 12;
dev_dbg(card->dev, "firmware build %d\n", hdspe->fw_build);
/* Serial number */
if (pci->vendor == PCI_VENDOR_ID_RME || hdspe->fw_build >= 200)
hdspe->serial = snd_hdspe_get_serial_rev2(hdspe);
else
hdspe->serial = snd_hdspe_get_serial_rev1(hdspe);
dev_dbg(card->dev, "serial nr %08d\n", hdspe->serial);
/* Card ID */
if (hdspe->serial != 0) { /* don't set ID if no serial (old PCI card) */
snprintf(card->id, sizeof(card->id), "HDSPe%08d",
hdspe->serial);
snd_card_set_id(card, card->id);
} else {
dev_warn(card->dev, "Card ID not set: no serial number.\n");
}
/* Mixer */
err = hdspe_init_mixer(hdspe);
if (err < 0)
return err;
/* TCO */
err = hdspe_init_tco(hdspe);
if (err < 0)
return err;
/* Methods, tables, registers */
err = hdspe_init(hdspe);
if (err < 0)
return err;
/* Create ALSA devices */
err = snd_hdspe_create_alsa_devices(card, hdspe);
if (err < 0)
return err;
if (hdspe->io_type != HDSPE_MADIFACE && hdspe->serial != 0) {
snprintf(card->shortname, sizeof(card->shortname), "%s_%08d",
hdspe->card_name, hdspe->serial);
snprintf(card->longname, sizeof(card->longname),
"%s S/N %08d at 0x%lx irq %d",
hdspe->card_name, hdspe->serial,
hdspe->port, hdspe->irq);
} else {
// TODO: MADIFACE really has no serial nr?
snprintf(card->shortname, sizeof(card->shortname), "%s",
hdspe->card_name);
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %d",
hdspe->card_name, hdspe->port, hdspe->irq);
}
return 0;
}
static int snd_hdspe_free(struct hdspe * hdspe)
{
if (hdspe->port) {
hdspe_stop_interrupts(hdspe);
cancel_work_sync(&hdspe->midi_work);
cancel_work_sync(&hdspe->status_work);
hdspe_terminate(hdspe);
hdspe_terminate_tco(hdspe);
hdspe_terminate_mixer(hdspe);
}
if (hdspe->irq >= 0)
free_irq(hdspe->irq, (void *) hdspe);
if (hdspe->iobase)
iounmap(hdspe->iobase);
if (hdspe->port)
pci_release_regions(hdspe->pci);
if (pci_is_enabled(hdspe->pci))
pci_disable_device(hdspe->pci);
return 0;
}
static void snd_hdspe_card_free(struct snd_card *card)
{
struct hdspe *hdspe = card->private_data;
if (hdspe)
snd_hdspe_free(hdspe);
}
static int snd_hdspe_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct hdspe *hdspe;
struct snd_card *card;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
err = snd_card_new(&pci->dev, index[dev], id[dev],
THIS_MODULE, sizeof(*hdspe), &card);
if (err < 0)
return err;
card->private_free = snd_hdspe_card_free;
hdspe = card->private_data;
hdspe->card = card;
hdspe->dev = dev;
hdspe->pci = pci;
err = snd_hdspe_create(hdspe);
if (err < 0)
goto free_card;
err = snd_card_register(card);
if (err < 0)
goto free_card;
pci_set_drvdata(pci, card);
dev++;
hdspe_start_interrupts(hdspe);
return 0;
free_card:
snd_card_free(card);
return err;
}
static void snd_hdspe_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
}
static struct pci_driver hdspe_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_hdspe_ids,
.probe = snd_hdspe_probe,
.remove = snd_hdspe_remove,
};
module_pci_driver(hdspe_driver);
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_core.h 0000664 0000000 0000000 00000134033 15060370513 0021633 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note
/**
* @file hdspe_core.h
* @brief RME HDSPe driver internal header.
*
* 20210728 ... 1207 - Philippe.Bekaert@uhasselt.be
*
* Based on earlier work of the other MODULE_AUTHORs,
* information kindly made available by RME (www.rme-audio.com),
* and hardware kindly made available by Amptec Belgium (www.amptec.be).
*/
/* TODO: undefine in production version */
#define DEBUG
#define CONFIG_SND_DEBUG
//#define TIME_INTERRUPT_INTERVAL
#define DAW_MODE
//#define PASSTHROUGH_MODE
#ifndef __SOUND_HDSPE_CORE_H
#define __SOUND_HDSPE_CORE_H
#include
#include
#include
#include
#include
// #define HDSPE_HDSP_REV 60 // HDSPe PCIe/ExpressCard
#define HDSPE_MADI_REV 210 // TODO: use
#define HDSPE_RAYDAT_REV 211
#define HDSPE_AIO_REV 212
#define HDSPE_MADIFACE_REV 213
#define HDSPE_AES_REV 240 // TODO: use
/* --- Write registers. ---
These are defined as byte-offsets from the iobase value. */
#define HDSPE_WR_SETTINGS 0 /* RayDAT, AIO, AIO Pro settings */
#define HDSPE_outputBufferAddress 32
#define HDSPE_inputBufferAddress 36
#define HDSPE_WR_FLASH (12*4) /* get serial nr on older firmware */
#define HDSPE_WR_CONTROL 64 /* main control register */
#define HDSPE_interruptConfirmation 96
#define HDSPE_WR_TCO 128 /* Time Code Option control */
#define HDSPE_WR_PLL_FREQ 256 /* for setting arbitrary clock values (DDS feature) */
#define HDSPE_midiDataOut0 352
#define HDSPE_midiDataOut1 356
#define HDSPE_midiDataOut2 368
#define HDSPE_eeprom_wr 384 /* for AES, not used in mac driver */
#define HDSPE_eeprom_rd (97*4) /* Unused */
/* DMA enable for 64 channels, only Bit 0 is relevant */
#define HDSPE_outputEnableBase 512 /* 512-767 input DMA */
#define HDSPE_inputEnableBase 768 /* 768-1023 output DMA */
// TODO: LOOPBACK
#define MADI_RECORD_OUTPUT (384*4) /* (384+i)*4 = channel i loopback */
/* 16 page addresses for each of the 64 channels DMA buffer in and out
(each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
#define HDSPE_pageAddressBufferOut 8192
#define HDSPE_pageAddressBufferIn (HDSPE_pageAddressBufferOut+64*16*4)
/* Hardware mixer: for each hardware output, there is a hardware input
* fader and a software playback fader. Regardless of the actual number of
* physical inputs and outputs of a card, the mixer always accomodates
* 64 hardware outputs, 64 hardware inputs and 64 software playbacks.
* So its size is 64*64*2 values. Each value is an uint16_t stored
* into a regular uint32_t i/o mapped register. 0 means mute. 32768 is
* unit gain.
* Hardware input index i fader for output index o
* is at byte address 4*(mixerBase + 128*o + i).
* Software playback index p fader for output index o
* is at byte address 4*(mixerBase + 128*o + 64 + p). */
#define HDSPE_MADI_mixerBase 32768 /* 32768-65535 for 2x64x64 Fader */
#define HDSPE_MATRIX_MIXER_SIZE 8192 /* = 2*64*64 * 4 Byte => 32kB */
/* --- Read registers. ---
These are defined as byte-offsets from the iobase value */
#define HDSPE_RD_STATUS0 0 /* all cards, different interpretation */
#define HDSPE_RD_STATUS1 64 /* RayDAT, AIO, AIO Pro */
#define HDSPE_RD_STATUS2 192 /* all cards, different interpretation */
#define HDSPE_RD_FBITS 128 /* all cards except MADI */
#define HDSPE_RD_TCO 256 /* Time Code Option status */
#define HDSPE_RD_BARCODE0 (104*4) /* serial number part 1, rev2 cards */
#define HDSPE_RD_BARCODE1 (105*4) /* serial number part 2, rev2 cards */
#define HDSPE_RD_FLASH (112*4) /* contains firmware build */
#define HDSPE_RD_PLL_FREQ 512 /* PLL frequency (DDS feature) */
#define HDSPE_midiDataIn0 360
#define HDSPE_midiDataIn1 364
#define HDSPE_midiDataIn2 372
#define HDSPE_midiDataIn3 376
/* status is data bytes in MIDI-FIFO (0-128) */
#define HDSPE_midiStatusOut0 384
#define HDSPE_midiStatusOut1 388
#define HDSPE_midiStatusOut2 400
#define HDSPE_midiStatusIn0 392
#define HDSPE_midiStatusIn1 396
#define HDSPE_midiStatusIn2 404
#define HDSPE_midiStatusIn3 408
/* Peak and RMS level meters are regular i/o-mapped registers, but offset
considerably from the rest. the peak registers are reset
when read; the least-significant 4 bits are full-scale counters;
the actual peak value is in the most-significant 24 bits.
*/
#define HDSPE_MADI_INPUT_PEAK 4096
#define HDSPE_MADI_PLAYBACK_PEAK 4352
#define HDSPE_MADI_OUTPUT_PEAK 4608
#define HDSPE_MADI_INPUT_RMS_L 6144
#define HDSPE_MADI_PLAYBACK_RMS_L 6400
#define HDSPE_MADI_OUTPUT_RMS_L 6656
#define HDSPE_MADI_INPUT_RMS_H 7168
#define HDSPE_MADI_PLAYBACK_RMS_H 7424
#define HDSPE_MADI_OUTPUT_RMS_H 7680
/* Register definitions.
* All registers are 32-bit little endian. */
#pragma scalar_storage_order little-endian
/**
* WR_CONTROL register (byte offset 64)
*
* Pos Mask MADI AES RayDAT/AIO/AIO Pro
*
* 00 1 START START START
* 01 2 LAT_0 LAT_0 LAT_0
* 02 4 LAT_1 LAT_1 LAT_1
* 03 8 LAT_2 LAT_2 LAT_2
* 04 10 Master Master
* 05 20 IE_AUDIO IE_AUDIO IE_AUDIO
* 06 40 freq0 freq0 freq0
* 07 80 freq1 freq1 freq1
* 08 100 freq2 freq2 freq2
* 09 200 PRO
* 10 400 tx_64ch EMP
* 11 800 AutoInp Dolby
* 12 1000
* 13 2000 SyncRef2
* 14 4000 inp_0
* 15 8000 inp_1
* 16 10000 SyncRef0 SyncRef0
* 17 20000 SyncRef1 SyncRef1
* 18 40000 SMUX SMUX
* 19 80000 CLR_TMS CLR_TMS
* 20 100000 WCK48 WCK48
* 21 200000 IEN2 IEN2 IEN2 (RayDAT TCO)
* 22 400000 IEN0 IEN0 IEN0
* 23 800000 IEN1 IEN1 IEN1 (AIO/Pro: TCO)
* 24 1000000 LineOut LineOut LineOut
* 25 2000000 HDSPe_float_format SyncRef3 HDSPe_float_format
* 26 4000000 IEN3 (TCO) DS_DoubleWire
* 27 8000000 QS_DoubleWire
* 28 10000000 QS_QuadWire
* 29 20000000
* 30 40000000 AES_float_format
* 31 80000000 freq3 freq3 freq3
*
* LAT_{0,1,2}: 3-bit value defining buffer size (and latency): 0=64 samples,
* 1=128, ..., 6=4096, 7=8192 on MADI/AES, 32 on RayDAT/AIO/AIO Pro.
*
*/
/* MIDI interrupt enable bitmasks */
#define HDSPE_Midi0InterruptEnable 0x00400000 /* MIDI 0 interrupt enable */
#define HDSPE_Midi1InterruptEnable 0x00800000 /* MIDI 1 interrupt enable */
#define HDSPE_Midi2InterruptEnable 0x00200000 /* MIDI 2 interrupt enable */
#define HDSPE_Midi3InterruptEnable 0x04000000 /* MIDI 3 interrupt enable */
/* Common to all cards */
struct hdspe_control_reg_common { __le32
START : 1, // start engine
LAT : 3, // buffer size is 2^n where n is defined by LAT
_04 : 1,
IE_AUDIO: 1, // what do you think?
freq : 2, // internal clock: 1=32KHz, 2=44.1KKhz, 3=48KHz
ds : 1, // double speed mode
_09 : 1,
_10 : 1,
_11 : 1,
_12 : 1,
_13 : 1,
_14 : 1,
_15 : 1,
_16 : 1,
_17 : 1,
_18 : 1,
_19 : 1,
_20 : 1,
IEN2 : 1, // MIDI 2 interrupt enable
IEN0 : 1, // MIDI 0 interrupt enable
IEN1 : 1, // MIDI 1 interrupt enable
LineOut : 1, // Enable/disable analog output
_25 : 1,
_26 : 1,
_27 : 1,
_28 : 1,
_29 : 1,
_30 : 1,
qs : 1; // quad speed mode
};
/* MADI */
struct hdspe_control_reg_madi { __le32
START : 1,
LAT : 3,
Master : 1, // Clock mode: 0=AutoSync, 1=Master
IE_AUDIO: 1,
freq : 2,
ds : 1,
_09 : 1,
tx_64ch : 1, // Output 64channel MODE=1, 56channelMODE=0
AutoInp : 1, // Auto Input (takeover) == Safe Mode, 0=off, 1=on
opt_out : 1, // unused
_13 : 1,
inp_0 : 1, // Input select 0=optical, 1=coaxial
inp_1 : 1, // Unused. Must be 0.
SyncRef : 2, // Preferred AutoSync reference:
// 0=WCK, 1=MADI, 2=TCO, 3=SyncIn
SMUX : 1, // Frame???
CLR_TMS : 1, // Clear AES/SPDIF channel status and track marker bits
WCK48 : 1, // Single speed word clock output
IEN2 : 1,
IEN0 : 1,
IEN1 : 1,
LineOut : 1,
FloatFmt: 1, // 32-bit LE floating point sample format
IEN3 : 1, // MIDI 3 interrupt enable (TCO)
_27 : 1,
_28 : 1,
_29 : 1,
_30 : 1,
qs : 1;
};
/* AES */
struct hdspe_control_reg_aes { __le32
START : 1,
LAT : 3,
Master : 1, // Clock mode: 0=AutoSync, 1=Master
IE_AUDIO: 1,
freq : 2,
ds : 1,
PRO : 1, // Professional
EMP : 1, // Emphasis
Dolby : 1, // Dolby = "NonAudio"
_12 : 1,
SyncRef2: 1, // Preferred AutoSync ref bit 2. Values:
_14 : 1, // 0=WCLK, 1=AES1, 2=AES2, 3=AES3, 4=AES4,
_15 : 1, // 5=AES5, 6=AES6, 7=AES7, 8=AES8, 9=TCO, 10=SyncIn
SyncRef0: 1, // Preferred AutoSync ref bit 0
SyncRef1: 1, // Preferred AutoSync ref bit 1
SMUX : 1,
CLR_TMS : 1, // Clear AES/SPDIF channel status and track marker bits
WCK48 : 1, // Single speed word clock output
IEN2 : 1,
IEN0 : 1,
IEN1 : 1,
LineOut : 1,
SyncRef3: 1, // Preferred AutoSync ref bit 3
ds_mode : 1, // DoubleSpeed mode: 0=single wire, 1=double wire
qs_mode : 2, // QuadSpeed mode: 0=single, 1=double, 2=quad wire
_29 : 1,
FloatFmt: 1, // 32-bit LE floating point sample format
qs : 1;
};
/* RayDAT / AIO / AIO Pro */
struct hdspe_control_reg_raio { __le32
START : 1,
LAT : 3,
_4 : 1,
IE_AUDIO: 1,
freq : 2,
ds : 1,
_09 : 1,
_10 : 1,
_11 : 1,
_12 : 1,
_13 : 1,
_14 : 1,
_15 : 1,
_16 : 1,
_17 : 1,
_18 : 1,
_19 : 1,
_20 : 1,
IEN2 : 1,
IEN0 : 1,
IEN1 : 1,
LineOut : 1,
FloatFmt: 1, // 32-bit LE floating point sample format
_26 : 1,
_27 : 1,
_28 : 1,
_29 : 1,
_30 : 1,
qs : 1;
};
union hdspe_control_reg {
__le32 raw; // register value
struct hdspe_control_reg_common common; // common fields
struct hdspe_control_reg_madi madi; // MADI fields
struct hdspe_control_reg_aes aes; // AES fields
struct hdspe_control_reg_raio raio; // RayDAT/AIO/AIO Pro fields
};
/**
* WR_SETTINGS register bits (byte offset 0) - RayDAT / AIO / AIO Pro only.
*/
struct hdspe_settings_reg_raio { __le32
Master : 1, // Clock Master (1) or AutoSync (0)
SyncRef : 4, // Preferred AutoSync reference:
// 0=WCLK, 1=AES, 2=SPDIF, 3=ADAT/ADAT1
// 4=ADAT2, 5=ADAT3, 6=ADAT4, 9=TCO, 10=SyncIn
Wck48 : 1, // Single speed word clock output = Single Speed in win
DS_DoubleWire : 1, // win , mac unused
QS_DoubleWire : 1, // win , mac unused
QS_QuadWire : 1, // win , mac unused
Madi_Smux : 1, // win , mac unused
Madi_64_Channels : 1, // win , mac unused
Madi_AutoInput : 1, // win , mac unused
Input : 2, // SPDIF in: 0=Optical, 1=Coaxial, 2=Internal
Spdif_Opt : 1, // SPDIF Optical out (ADAT4 optical out on RayDAT)
Pro : 1, // SPDIF Professional format out
clr_tms : 1, // Clear AES/SPDIF channel status and track marker bits
AEB1 : 1, // ADAT1 internal (use with TEB or AEB extension boards)
AEB2 : 1, // RayDAT ADAT2 internal, AIO: S/PDIF emphasis??
LineOut : 1, // AIO Pro only (AD/DA/PH power on/off?)
AD_GAIN : 2, // Input Level 0-2 (AIO) / 0-3 (AIO Pro)
DA_GAIN : 2, // Output Level 0-2 (AIO) / 0-3 (AIO Pro)
PH_GAIN : 2, // Phones Level 0-2 (AIO) / 0-1 (AIO Pro)
Sym6db : 1, // Analog output: 0=RCA, 1=XLR
_27 : 1,
_28 : 1,
_29 : 1,
_30 : 1,
_21 : 1;
};
union hdspe_settings_reg {
__le32 raw; // register value
struct hdspe_settings_reg_raio raio; // RayDAT / AIO / AIO Pro bits
};
/**
* Clock logic:
*
* If the card is running in master clock mode or no valid AutoSync
* reference is present, the internal clock is used ("internal frequency").
* If in AutoSync mode, and a valid AutoSync reference is present, the card
* synchronises to that AutoSync reference ("external frequency").
*
* In all cases, the RD_PLL_FREQ register indicates at what exact frequency
* the card is running, relative to a fixed oscillator frequency that depends
* on the type of card - see hdspe_read_system_sample_rate() - and up to
* double/quad speed rate multiplication as set in the WR_CONTROL register.
*
* The frequency class of the internal clock is in the WR_CONTROL register
* (see above). When reduced to single speed mode, it shall always correspond
* to the PLL frequency rounded to the nearest of 32KHz, 44.1KHz, 48KHz.
* If in Master clock mode, the internal clock frequency is fine tuned through
* the PLL frequency control register (WR_PLL_FREQ), see
* hdspe_write_system_sample_rate().
*
* MADI cards provide frequency and lock and sync status of the MADI input
* and the external frequency (up to single/double/quad speed rate setting).
* The MADI input lock/sync/frequency status is in the RD_STATUS0 register. The
* external frequency and selected AutoSync reference is in RD_STATUS2.
* lock/sync status of tco and sync in are in the RD_STATUS register,
* lock/sync status of the word clock is in the RD_STATUS2 register.
*
* Other cards provide frequency and lock/sync status of the word clock, tco,
* sync in, and all digital audio inputs individually, in different
* registers depending on the type of card.
*
* The frequency class values reported in the status registers are always
* 4-bit values, with the following interpretation:
* 1=32KHz, 2=44.1KHz, 3=48KHz,
* 4=64KHz, 5=88.2KHz, 6=96KHz,
* 7=128KHz, 8=176.4KHz, 9=192KHz
* other values indicate no lock.
*
* All cards report whether there is a valid external clock reference or not,
* and if so, which reference is used for synchronisation.
* The selected AutoSync reference is in different places and has different
* semantics for MADI, AES or RayDAT/AIO/AIO Pro. For MADI, it is a 3-bit
* value in the RD_STATUS2 register. For other cards it is a 4-bit value.
* For AES, it is sync_ref{3,2,1,0} in the RD_STATUS0 register.
* For RayDAT/AIO/AIO Pro it is in the RD_STATUS1 register.
*
* So, MADI cards readily report their external frequency, if in sync.
* For other cards, we need to read the selected AutoSync reference. The
* external frequency is the frequency of thet AutoSync reference.
*
* The external frequency thus found needs conversion of the single/double/
* quad speed mode as set in the WR_CONTROL register.
*/
/**
* RD_STATUS0 register (byte offset 0) bits:
*
* Pos Mask MADI AES RayDAT/AIO/AIO Pro
*
* 00 1 IRQ IRQ IRQ
* 01 2 rx_64ch tco_freq0
* 02 4 AB_int tco_freq1
* 03 8 madi_lock tco_freq2
* 04 10 tco_freq3
* 05 20 madi_tco_lock mIRQ2_AES
* FFC0 hw buffer pointer hw buffer pointer hw buffer pointer
* 16 10000 madi_sync_in_lock sync_ref0
* 17 20000 madi_sync_in_sync sync_ref1
* 18 40000 madi_sync sync_ref2
* 19 80000 sync_ref3
* 20 100000 wclk_sync
* 21 200000 mIRQ3 wclk_lock
* 22 400000 F_0 wclk_freq0
* 23 800000 F_1 wclk_freq1
* 24 1000000 F_2 wclk_freq2
* 25 2000000 F_3 wclk_freq3
* 26 4000000 BUF_ID BUF_ID BUF_ID
* 27 8000000 tco_detect tco_detect
* 28 10000000 tco_sync tco_sync
* 29 20000000 mIRQ2 aes_tco_lock mIRQ2 (RayDAT TCO)
* 30 40000000 mIRQ0 mIRQ0 mIRQ0
* 31 80000000 mIRQ1 mIRQ1 mIRQ1 (AIO/Pro: TCO)
*/
/* MIDI interrupt pending */
#define HDSPE_midi0IRQPending 0x40000000
#define HDSPE_midi1IRQPending 0x80000000
#define HDSPE_midi2IRQPending 0x20000000
#define HDSPE_midi2IRQPendingAES 0x00000020 /* AES with TCO */
#define HDSPE_midi3IRQPending 0x00200000 /* MADI with TCO */
/* common */
struct hdspe_status0_reg_common { __le32
IRQ : 1, // Audio interrupt pending
_01 : 1,
_02 : 1,
_03 : 1,
_04 : 1,
_05 : 1,
BUF_PTR : 10, // Most significant bits of the hardware buffer
// pointer. Since 64-byte accurate, the least
// significant 6 bits are 0. Little endian!
_16 : 1,
_17 : 1,
_18 : 1,
_19 : 1,
_20 : 1,
_21 : 1,
_22 : 1,
_23 : 1,
_24 : 1,
_25 : 1,
BUF_ID : 1, // (Double) buffer ID, toggles with interrupt
_27 : 1,
_28 : 1,
_29 : 1,
mIRQ0 : 1, // MIDI 0 interrupt pending
mIRQ1 : 1; // MIDI 1 interrupt pending
};
/* MADI */
struct hdspe_status0_reg_madi { __le32
IRQ : 1,
rx_64ch : 1, // Input 64chan. MODE=1, 56chn MODE=0
AB_int : 1, // Input channel: 0=optical, 1=coaxial
madi_lock : 1, // MADI input lock status
_04 : 1,
tco_lock : 1, // TCO lock status
BUF_PTR : 10,
sync_in_lock : 1, // Sync In lock status
sync_in_sync : 1, // Sync In sync status
madi_sync : 1, // MADI input sync status
_19 : 1,
_20 : 1,
mIRQ3 : 1, // MIDI 3 interrupt pending
madi_freq : 4, // MADI input frequency class
BUF_ID : 1,
tco_detect : 1, // TCO present
tco_sync : 1, // TCO is in sync
mIRQ2 : 1, // MIDI 2 interrupt pending
mIRQ0 : 1,
mIRQ1 : 1;
};
/* AES */
struct hdspe_status0_reg_aes { __le32
IRQ : 1,
tco_freq : 4, // TCO frequency class
mIRQ2 : 1, // MIDI 2 interrupt pending
BUF_PTR : 10,
sync_ref : 4, // active AutoSync reference: 0=WCLK, 1=AES1,
// ... 8=AES8, 9=TCO, 10=SyncIn, other=None
wc_sync : 1, // word clock sync status
wc_lock : 1, // word clock lock status
wc_freq : 4, // word clock frequency class
BUF_ID : 1,
tco_detect : 1, // TCO present
tco_sync : 1, // TCO sync status
tco_lock : 1, // TCO lock status
mIRQ0 : 1,
mIRQ1 : 1;
};
/* RayDAT / AIO / AIO Pro */
struct hdspe_status0_reg_raio { __le32
IRQ : 1,
_01 : 1,
_02 : 1,
_03 : 1,
_04 : 1,
_05 : 1,
BUF_PTR : 10,
_16 : 1,
_17 : 1,
_18 : 1,
_19 : 1,
_20 : 1,
_21 : 1,
_22 : 1,
_23 : 1,
_24 : 1,
_25 : 1,
BUF_ID : 1,
_27 : 1,
_28 : 1,
mIRQ2 : 1, // MIDI 2 interrupt pending (RayDAT TCO MIDI)
mIRQ0 : 1,
mIRQ1 : 1;
};
union hdspe_status0_reg {
__le32 raw; // register value
struct hdspe_status0_reg_common common; // common bits
struct hdspe_status0_reg_madi madi; // MADI bits
struct hdspe_status0_reg_aes aes; // AES bits
struct hdspe_status0_reg_raio raio; // RayDAT/AIO/AIO Pro bits
};
/**
* RD_STATUS2 register (byte offset 192)
*
* Pos Mask MADI AES RayDAT/AIO/AIO Pro
*
* 00 1 spdif_lock7
* 01 2 spdif_lock6
* 02 4 spdif_lock5
* 03 8 wc_lock spdif_lock4
* 04 10 wc_sync spdif_lock3
* 05 20 inp_freq0 spdif_lock2
* 06 40 inp_freq1 spdif_lock1 s2_tco_detect
* 07 80 inp_freq2 spdif_lock0 s2_AEBO_D
* 08 100 SelSyncRef0 spdif_sync7 s2_AEBI_D
* 09 200 SelSyncRef1 spdif_sync6
* 10 400 SelSyncRef2 spdif_sync5 s2_sync_in_lock
* 11 800 inp_freq3 spdif_sync4 s2_sync_in_sync
* 12 1000 spdif_sync3 s2_sync_in_freq0
* 13 2000 spdif_sync2 s2_sync_in_freq1
* 14 4000 spdif_sync1 s2_sync_in_freq2
* 15 8000 spdif_sync0 s2_sync_in_freq3
* 16 10000 aes_mode0
* 17 20000 aes_mode1
* 18 40000 aes_mode2
* 19 80000 aes_mode3
* 20 100000 sync_in_lock
* 21 200000 sync_in_sync
* 22 400000 sync_in_freq0
* 23 800000 sync_in_freq1
* 24 1000000 sync_in_freq2
* 25 2000000 sync_in_freq3
*/
/* MADI */
struct hdspe_status2_reg_madi { __le32
_00 : 1,
_01 : 1,
_02 : 1,
wc_lock : 1, // Word clock lock status
wc_sync : 1, // Word clock sync status
inp_freq0 : 1, // AutoSync frequency class (4 bits, scattered)
inp_freq1 : 1, // "
inp_freq2 : 1, // "
sync_ref : 3, // Active AutoSync ref: 0=WCLK, 1=MADI, 2=TCO,
inp_freq3 : 1, // 3=Sync In, other=None
_12 : 1,
_13 : 1,
_14 : 1,
_15 : 1,
__2 : 8,
__3 : 8;
};
/* AES */
struct hdspe_status2_reg_aes { __le32
lock : 8, // bit 0=AES8, 1=AES7 ... 7=AES1 lock status
sync : 8, // bit 0=AES8, 1=AES7 ... 7=AES1 sync status
aes_mode : 4,
sync_in_lock : 1, // Sync In lock status
sync_in_sync : 1, // Sync In sync status
sync_in_freq : 4, // Sync In frequency class
_26 : 1,
_27 : 1,
_28 : 1,
_29 : 1,
_30 : 1,
_31 : 1;
};
/* RayDAT / AIO / AIO Pro */
struct hdspe_status2_reg_raio { __le32
_00 : 1,
_01 : 1,
_02 : 1,
_03 : 1,
_04 : 1,
_05 : 1,
tco_detect : 1, // TCO detected
AEBO_D : 1, // Output Audio Extension Board NOT present
AEBI_D : 1, // Input Audio Extension Board NOT present
_09 : 1,
sync_in_lock : 1, // SyncIn lock status
sync_in_sync : 1, // SyncIn sync status
sync_in_freq : 4, // SyncIn frequency class
__2 : 8,
__3 : 8;
};
union hdspe_status2_reg {
__le32 raw; // register value
struct hdspe_status2_reg_madi madi; // MADI fields
struct hdspe_status2_reg_aes aes; // AES fields
struct hdspe_status2_reg_raio raio; // RayDAT / AIO / AIO Pro fields
};
/**
* RD_STATUS1 register (byte offset 64) - RayDAT/AIO/AIO Pro only.
*/
struct hdspe_status1_reg_raio { __le32
lock : 8, // bit 0=AES, 1=SPDIF, 2..5=ADAT1..4 lock status
sync : 8, // bit 0=AES, 1=SPDIF, 2..5=ADAT1..4 sync status
wc_freq : 4, // word clock frequency class
tco_freq : 4, // TCO frequency class
wc_lock : 1, // word clock lock status
wc_sync : 1, // word clock sync status
tco_lock : 1, // TCO lock status
tco_sync : 1, // TCO sync status
sync_ref : 4; // Active AutoSync ref: 0=WCLK, 1=AES, 2=SPDIF
// 3=ADAT1, 4=ADAT2, 5=ADAT3, 6=ADAT4,
// 9=TCO, 10=SyncIn, 15=Internal
};
union hdspe_status1_reg {
__le32 raw; // register value
struct hdspe_status1_reg_raio raio; // RayDAT / AIO / AIO Pro fields
};
/**
* RD_FBITS register (byte offset 192) contains audio input clock rate values
* for AES and RayDAT/AIO/AIO Pro digital audio inputs.
*
* Bit mask AES RayDAT/AIO/AIO Pro
*
* 0000000f AES1 AES
* 000000f0 AES2 SPDIF
* 00000f00 AES3 ADAT (AIO/AIO Pro), ADAT1 (RayDAT)
* 0000f000 AES4 ADAT2 (RayDAT)
* 000f0000 AES5 ADAT3 (RayDAT)
* 00f00000 AES6 ADAT4 (RayDAT)
* 0f000000 AES7
* f0000000 AES8
*
* Frequency values: 1=32KHz, 2=44.1KHz, ... 9=192KHz, other=NoLock */
/* end of register definitions */
#pragma scalar_storage_order default
/**
* Compose HSDPE_SYNC_STATUS from lock, sync and present bitfields in
* the various status registers.
*/
#define HDSPE_MAKE_SYNC_STATUS(lock, sync, present) \
(!(present) ? HDSPE_SYNC_STATUS_NOT_AVAILABLE \
: !(lock) ? HDSPE_SYNC_STATUS_NO_LOCK \
: !(sync) ? HDSPE_SYNC_STATUS_LOCK \
: HDSPE_SYNC_STATUS_SYNC)
/**
* Get frequency class from RD_FBITS register
*/
#define HDSPE_FBITS_FREQ(reg, i) \
(((reg) >> ((i)*4)) & 0xF)
/* --------------------------------------------------------- */
/* max. 4 MIDI ports per card */
#define HDSPE_MAX_MIDI 4
struct hdspe_midi {
struct timer_list timer;
spinlock_t lock;
struct hdspe *hdspe;
const char* portname;
int id;
int dataIn;
int statusIn;
int dataOut;
int statusOut;
int ie;
int irq;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *input;
struct snd_rawmidi_substream *output;
int pending; /* interrupt is pending */
int istimer; /* timer in use */
};
//#define DEBUG_LTC
//#define DEBUG_MTC
struct hdspe_tco {
spinlock_t lock;
/* cached control registers */
u32 reg[4];
enum hdspe_tco_source input;
enum hdspe_ltc_frame_rate ltc_fps;
enum hdspe_bool ltc_drop;
enum hdspe_tco_sample_rate sample_rate;
enum hdspe_pull pull;
enum hdspe_wck_conversion wck_conversion;
enum hdspe_bool term;
enum hdspe_speed wck_out_speed;
/* LTC out control */
u32 ltc_out; /* requested start LTC for output */
u64 ltc_out_frame_count; /* start LTC output at this frame count */
bool ltc_set; /* time code set - need reset at next period */
bool ltc_run; /* time code output is running */
bool ltc_flywheel; /* loop back time code output to input */
/* Current LTC in */
bool ltc_changed; /* set when new LTC has been received */
u32 ltc_in; /* current LTC: last parsed LTC + 1 frame */
u64 ltc_time; /* frame_count at start of current period */
u64 ltc_in_frame_count; /* frame count at start of current LTC */
/* for status polling */
struct hdspe_tco_status last_status;
/* for measuring the actual LTC In fps and pull factor */
#define LTC_CACHE_SIZE 60
u64 prev_ltc_time; /* nanosecond timestamp of previous MTC irq */
u64 ltc_duration_sum; /* sum of observed LTC frame durations */
u32 ltc_duration[LTC_CACHE_SIZE]; /* observed LTC frame durations */
u32 ltc_count; /* number of received LTC frames */
u32 ltc_in_pullfac; /* actual LTC in pull factor - estimated */
u32 last_ltc_in_pullfac; /* for change notification */
#ifdef DEBUG_MTC
u32 mtc; /* current MIDI time code */
#endif /*DEBUG_MTC*/
u8 fw_version; /* TCO firmware version */
};
/**
* Card-dependent methods. Initialized by hdspe_init_[madi|aes|raio].
*/
struct hdspe_methods {
void (*get_card_info)(struct hdspe* hdspe, struct hdspe_card_info* inf);
void (*read_status)(struct hdspe* hdspe, struct hdspe_status* status);
void (*set_float_format)(struct hdspe* hdspe, bool val);
bool (*get_float_format)(struct hdspe* hdspe);
void (*read_proc)(struct snd_info_entry*, struct snd_info_buffer*);
enum hdspe_clock_source (*get_autosync_ref)(struct hdspe*);
enum hdspe_clock_mode (*get_clock_mode)(struct hdspe*);
void (*set_clock_mode)(struct hdspe*, enum hdspe_clock_mode);
enum hdspe_clock_source (*get_pref_sync_ref)(struct hdspe*);
void (*set_pref_sync_ref)(struct hdspe*, enum hdspe_clock_source);
bool (*check_status_change)(struct hdspe*,
struct hdspe_status* old_status,
struct hdspe_status* new_status);
};
/**
* Card dependant tables. Initialized by hdspe_init_[madi|aes|raio].
*/
struct hdspe_tables {
/* See hdspe_init_autosync_tables() */
int autosync_count;
const char* autosync_texts[HDSPE_CLOCK_SOURCE_COUNT];
enum hdspe_clock_source autosync_idx2ref[HDSPE_CLOCK_SOURCE_COUNT];
int autosync_ref2idx[HDSPE_CLOCK_SOURCE_COUNT];
/* Initialized by hdspe__init() and assigned to
* hdspe::port_names_in, hdspe::port_names_out, hdspe::channel_map_in,
* hdspe_channel_map_out, hdspe::max_channels_in,
* hdspe::max_channels_out by hdspe_set_channel_map(). */
const char * const *port_names_in_ss;
const char * const *port_names_in_ds;
const char * const *port_names_in_qs;
const char * const *port_names_out_ss;
const char * const *port_names_out_ds;
const char * const *port_names_out_qs;
const signed char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
const signed char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
unsigned char ss_in_channels;
unsigned char ds_in_channels;
unsigned char qs_in_channels;
unsigned char ss_out_channels;
unsigned char ds_out_channels;
unsigned char qs_out_channels;
const char * const *clock_source_names;
};
/* status element ids for status change notification */
struct hdspe_ctl_ids {
// TODO: there's probably a better way to query whether
// we're running or changing buffer size, without control elements.
struct snd_ctl_elem_id* running;
struct snd_ctl_elem_id* buffer_size;
struct snd_ctl_elem_id* status_polling;
struct snd_ctl_elem_id* internal_freq;
struct snd_ctl_elem_id* raw_sample_rate;
struct snd_ctl_elem_id* dds;
struct snd_ctl_elem_id* autosync_ref;
struct snd_ctl_elem_id* autosync_status;
struct snd_ctl_elem_id* autosync_freq;
/* MADI */
struct snd_ctl_elem_id* external_freq;
struct snd_ctl_elem_id* madi_input_source;
struct snd_ctl_elem_id* madi_rx_64ch;
/* TCO */
struct snd_ctl_elem_id* ltc_in;
struct snd_ctl_elem_id* ltc_valid;
struct snd_ctl_elem_id* ltc_in_fps;
struct snd_ctl_elem_id* ltc_in_drop;
struct snd_ctl_elem_id* ltc_in_pullfac;
struct snd_ctl_elem_id* video;
struct snd_ctl_elem_id* wck_valid;
struct snd_ctl_elem_id* wck_speed;
struct snd_ctl_elem_id* tco_lock;
struct snd_ctl_elem_id* ltc_run;
struct snd_ctl_elem_id* ltc_jam_sync;
struct snd_ctl_elem_id* video_in_fps;
/* struct snd_ctl_elem_id* wck_out_rate; */
};
struct hdspe {
struct pci_dev *pci; /* pci info */
int vendor_id; /* PCI vendor ID: Xilinx or RME */
int dev; /* hardware vars... */
int irq;
unsigned long port;
void __iomem *iobase;
u16 firmware_rev; /* determines io_type (card model) */
u16 reserved;
u32 fw_build; /* firmware build */
u32 serial; /* serial nr */
enum hdspe_io_type io_type; /* MADI, AES, RAYDAT, AIO or AIO_PRO */
char *card_name; /* for procinfo */
struct hdspe_methods m; /* methods for the card model */
struct hdspe_tables t; /* tables for the card model */
/* ALSA devices */
struct snd_card *card; /* one card */
struct snd_pcm *pcm; /* has one pcm */
struct snd_hwdep *hwdep; /* and a hwdep for additional ioctl */
/* Only one playback and/or capture stream */
struct snd_pcm_substream *capture_substream;
struct snd_pcm_substream *playback_substream;
/* MIDI */
struct hdspe_midi midi[HDSPE_MAX_MIDI];
struct work_struct midi_work;
__le32 midiInterruptEnableMask;
__le32 midiIRQPendingMask;
int midiPorts; /* number of MIDI ports */
/* Check for status changes, status_polling times per second, if >0.
* Status polling is disabled if 0.
* Initially, it is 0 and needs to be enabled by the client.
* hdspe_status_work() resets it to 0 when detecting a change,
* notifying the client with a "Status Polling"
* control notification event and notifications for all
* changed status control elements.
* Status polling is also automatically disabled by the driver,
* with "Status Polling" notification, after 2 seconds without
* changes. The client must re-enable by setting "Status Polling"
* to a non-zero value to re-enable it. */
int status_polling;
struct work_struct status_work;
unsigned long last_status_jiffies;
unsigned long last_status_change_jiffies;
struct hdspe_status last_status;
struct hdspe_ctl_ids cid; /* control ids to be notified */
/* Mixer vars */
/* full mixer accessible over mixer ioctl or hwdep-device */
struct hdspe_mixer *mixer;
struct hdspe_peak_rms peak_rms;
/* fast alsa mixer */
struct snd_kcontrol *playback_mixer_ctls[HDSPE_MAX_CHANNELS];
/* but input to much, so not used */
struct snd_kcontrol *input_mixer_ctls[HDSPE_MAX_CHANNELS];
/* Optional Time Code Option module handle (NULL if absent) */
struct hdspe_tco *tco;
#ifdef DEBUG_LTC
struct timer_list tco_timer;
#endif /*DEBUG_LTC*/
/* Channel map and port names - set by hdspe_set_channel_map() */
unsigned char max_channels_in;
unsigned char max_channels_out;
const signed char *channel_map_in;
const signed char *channel_map_out;
const char * const *port_names_in;
const char * const *port_names_out;
unsigned char *playback_buffer; /* suitably aligned address */
unsigned char *capture_buffer; /* suitably aligned address */
pid_t capture_pid; /* process id which uses capture */
pid_t playback_pid; /* process id which uses capture */
int running; /* running status */
spinlock_t lock;
int irq_count; /* for debug */
#ifdef TIME_INTERRUPT_INTERVAL
u64 last_interrupt_time;
#endif /*TIME_INTERRUPT_INTERVAL*/
/* Register cache */
struct reg {
union hdspe_control_reg control;
union hdspe_settings_reg settings;
__le32 pll_freq;
union hdspe_status0_reg status0; /* read at every interrupt */
} reg;
u64 frame_count; /* current period frame counter */
u32 hw_pointer_wrap_count; /* hw pointer wrapped this many times */
u32 last_hw_pointer; /* previous period hw pointer */
u32 hw_buffer_size; /* sample buffer size, in nr of samples */
u32 period_size; /* current period size, in nr of samples */
};
/**
* Write/read to/from HDSPE with Adresses in Bytes
* not words but only 32Bit writes are allowed.
*/
static inline __attribute__((always_inline))
void hdspe_write(struct hdspe * hdspe, u32 reg, __le32 val)
{
writel(val, hdspe->iobase + reg);
}
static inline __attribute__((always_inline))
__le32 hdspe_read(struct hdspe * hdspe, u32 reg)
{
return readl(hdspe->iobase + reg);
#ifdef FROM_WIN_DRIVER
if (!deviceExtension->bShutdown) {
value = READ_REGISTER_ULONG(deviceExtension->MemBase+offset);
if (value == 0xFFFFFFFF && !(offset >= 256 && offset < 320)) { // surprise removal
value = 0;
}
#endif
}
static inline __attribute__((always_inline))
void hdspe_write_control(struct hdspe* hdspe)
{
hdspe_write(hdspe, HDSPE_WR_CONTROL, hdspe->reg.control.raw);
}
static inline __attribute__((always_inline))
void hdspe_write_settings(struct hdspe* hdspe)
{
hdspe_write(hdspe, HDSPE_WR_SETTINGS, hdspe->reg.settings.raw);
}
static inline __attribute__((always_inline))
void hdspe_write_pll_freq(struct hdspe* hdspe)
{
hdspe_write(hdspe, HDSPE_WR_PLL_FREQ, hdspe->reg.pll_freq);
}
// status0 register is read at every interrupt if audio is started and
// audio interrupt enabled. We make hdspe_read_status() return the
// value of the status register at the time of the interrupt in that
// case, thus reducing the number of times the hardware is read to a
// minimum as well as reducing the need for locking.
static inline __attribute__((always_inline))
union hdspe_status0_reg hdspe_read_status0_nocache(struct hdspe* hdspe)
{
union hdspe_status0_reg reg;
reg.raw = hdspe_read(hdspe, HDSPE_RD_STATUS0);
return reg;
}
static inline __attribute__((always_inline))
bool hdspe_is_running(struct hdspe* hdspe)
{
return hdspe->reg.control.common.START &&
hdspe->reg.control.common.IE_AUDIO;
}
static inline __attribute__((always_inline))
union hdspe_status0_reg hdspe_read_status0(struct hdspe* hdspe)
{
return hdspe_is_running(hdspe) ? hdspe->reg.status0 :
hdspe_read_status0_nocache(hdspe);
}
static inline __attribute__((always_inline))
union hdspe_status1_reg hdspe_read_status1(struct hdspe* hdspe)
{
union hdspe_status1_reg reg;
reg.raw = hdspe_read(hdspe, HDSPE_RD_STATUS1);
return reg;
}
static inline __attribute__((always_inline))
union hdspe_status2_reg hdspe_read_status2(struct hdspe* hdspe)
{
union hdspe_status2_reg reg;
reg.raw = hdspe_read(hdspe, HDSPE_RD_STATUS2);
return reg;
}
static inline __attribute__((always_inline))
u32 hdspe_read_fbits(struct hdspe* hdspe)
{
return le32_to_cpu(hdspe_read(hdspe, HDSPE_RD_FBITS));
}
static inline __attribute__((always_inline))
u32 hdspe_read_pll_freq(struct hdspe* hdspe)
{
return le32_to_cpu(hdspe_read(hdspe, HDSPE_RD_PLL_FREQ));
}
/**
* hdspe_pcm.c
*/
extern int snd_hdspe_create_pcm(struct snd_card *card,
struct hdspe *hdspe);
/* Current period size in samples. */
extern u32 hdspe_period_size(struct hdspe *hdspe);
/* Get current hardware frame counter. Wraps every 16K frames or sooner
* for MADI and AES cards. */
extern snd_pcm_uframes_t hdspe_hw_pointer(struct hdspe *hdspe);
/* Called right from the interrupt handler in order to update
* hdspe->frame_count.
* In absence of xruns, the frame counter increments by
* hdspe_period_size() frames each period. This routine will correctly
* determine the frame counter even in the presence of xruns or late
* interrupt handling, as long as the hardware pointer did not wrap more
* than once since the previous invocation. */
extern void hdspe_update_frame_count(struct hdspe* hdspe);
/**
* hdspe_midi.c
*/
extern void hdspe_init_midi(struct hdspe* hdspe,
int count, struct hdspe_midi *list);
extern void hdspe_terminate_midi(struct hdspe* hdspe);
extern int snd_hdspe_create_midi(struct snd_card *card,
struct hdspe *hdspe, int id);
extern void hdspe_midi_work(struct work_struct *work);
/**
* hdspe_hwdep.c
*/
extern int snd_hdspe_create_hwdep(struct snd_card *card,
struct hdspe *hdspe);
extern void hdspe_get_card_info(struct hdspe* hdspe, struct hdspe_card_info *s);
/**
* hdspe_proc.c
*/
extern void snd_hdspe_proc_init(struct hdspe *hdspe);
/* Read hdspe_status from hardware and prints properties common to all
* HDSPe cards, to be done before printing card-specific state. */
extern void hdspe_proc_read_common(struct snd_info_buffer *buffer,
struct hdspe* hdspe,
struct hdspe_status* s);
/* Proc read common stuff to be done after printing card-specific state */
void hdspe_proc_read_common2(struct snd_info_buffer *buffer,
struct hdspe* hdspe,
struct hdspe_status* s);
/* Prints the fields of the FBITS register */
extern void hdspe_iprint_fbits(struct snd_info_buffer *buffer,
const char* name, u32 fbits);
/* Prints the bits that are on in a register. */
extern void hdspe_iprintf_reg(struct snd_info_buffer *buffer,
const char* name, __le32 reg,
const char* const *bitNames);
#ifdef CONFIG_SND_DEBUG
#define IPRINTREG(buffer, name, reg, bitNames) \
hdspe_iprintf_reg(buffer, name, reg, bitNames);
#else /* not CONFIG_SND_DEBUG */
#define IPRINTREG(buffer, name, reg, bitNames) \
hdspe_iprintf_reg(buffer, name, reg, 0);
#endif /* CONFIG_SND_DEBUG */
/**
* hdspe_control.c
*/
extern void hdspe_init_autosync_tables(struct hdspe* hdspe,
int nr_autosync_opts,
enum hdspe_clock_source* autosync_opts);
extern int snd_hdspe_create_controls(struct snd_card *card,
struct hdspe *hdspe);
extern int hdspe_add_controls(struct hdspe *hdspe,
int count, const struct snd_kcontrol_new *list);
extern struct snd_kcontrol* hdspe_add_control(struct hdspe* hdspe,
const struct snd_kcontrol_new* newctl);
/* Same, filling in control element snd_ctl_elem_id* in ctl_id */
extern int hdspe_add_control_id(struct hdspe* hdspe,
const struct snd_kcontrol_new* nctl,
struct snd_ctl_elem_id** ctl_id);
extern void hdspe_status_work(struct work_struct* work);
#define HDSPE_CTL_NOTIFY(prop) \
snd_ctl_notify(hdspe->card, SNDRV_CTL_EVENT_MASK_VALUE, \
hdspe->cid.prop);
/**
* hdspe_mixer.c
*/
extern int hdspe_init_mixer(struct hdspe* hdspe);
extern void hdspe_terminate_mixer(struct hdspe* hdspe);
extern int hdspe_create_mixer_controls(struct hdspe* hdspe);
#ifdef OLDSTUFF
extern int hdspe_update_simple_mixer_controls(struct hdspe * hdspe);
#endif /*OLDSTUFF*/
extern void hdspe_mixer_read_proc(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
extern void hdspe_mixer_update_channel_map(struct hdspe* hdspe);
/**
* hdspe_tco.c
*/
extern int hdspe_init_tco(struct hdspe* hdspe);
extern void hdspe_terminate_tco(struct hdspe* hdspe);
extern int hdspe_create_tco_controls(struct hdspe* hdspe);
extern void hdspe_tco_read_status(struct hdspe* hdspe,
struct hdspe_tco_status* status);
extern void snd_hdspe_proc_read_tco(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
/* Called from the MIDI input handler, whenever an MTC message comes in */
extern void hdspe_tco_mtc(struct hdspe* hdspe,
const u8* data, int count);
/* Scheduled from the audio interrupt handler */
extern void hdspe_tco_period_elapsed(struct hdspe* hdspe);
/* TCO module status polling */
extern bool hdspe_tco_notify_status_change(struct hdspe* hdspe);
/* Set "app" sample rate on TCO module, when sound card sample rate changes. */
extern void hdspe_tco_set_app_sample_rate(struct hdspe* hdspe);
/**
* hdspe_common.c
*/
/* Get name of the clock source */
extern const char* const hdspe_clock_source_name(struct hdspe* hdspe, int i);
/* Get name of the frequency class */
extern const char* const hdspe_freq_name(enum hdspe_freq f);
/* Get frequency class frame rate */
extern u32 hdspe_freq_sample_rate(enum hdspe_freq f);
/* Get the frequency class best representing the given rate */
enum hdspe_freq hdspe_sample_rate_freq(u32 rate);
/* Sets the channel map and port names according the indicated speed mode. */
extern void hdspe_set_channel_map(struct hdspe* hdspe, enum hdspe_speed speed);
/* Get the current internal frequency class (single speed
* frequency and speed mode control register bits encoded into
* a single frequency class value. */
extern enum hdspe_freq hdspe_internal_freq(struct hdspe* hdspe);
/* Write the internal frequency (single speed frequency and speed
* mode control register bits). No checks, no channel map update.
* Returns 1 if changed and 0 if not. */
extern int hdspe_write_internal_freq(struct hdspe* hdspe, enum hdspe_freq f);
/* Get the current speed mode */
extern enum hdspe_speed hdspe_speed_mode(struct hdspe* hdspe);
/* Get the current speed factor: 1, 2 or 4 */
extern int hdspe_speed_factor(struct hdspe* hdspe);
/* Convert frequency class to speed mode */
extern enum hdspe_freq hdspe_speed_adapt(enum hdspe_freq f,
enum hdspe_speed speed_mode);
/* Get WR_PLL_FREQ (DDS) register value valid range (controls internal sampe
* rate in the range 27000 ... 207000/4 Hz) */
extern void hdspe_dds_range(struct hdspe* hdspe, u32* ddsmin, u32* ddsmax);
/* Get DDS register value */
extern u32 hdspe_get_dds(struct hdspe*);
/* Check and write DDS register value. Return -EINVAL if dds is out
* of range, 0 if no change, 1 if change. */
extern int hdspe_write_dds(struct hdspe*, u32 dds);
/*Writes the desired internal clock pitch to the WR_PLL_FREQ.
* Pitch is in parts per milion relative to the current control registers
* single speed frequency setting. If ppm is 1000000, the cards internal
* clock will run at exactly the internal frequency set in the control
* register. If pitch < 1000000, the card will run slower. If > 1000000 it
* will run faster. Returns 1 if changed and 0 if not. */
extern int hdspe_write_internal_pitch(struct hdspe* hdspe, int ppm);
/* Return the cached value of the internal pitch. */
extern u32 hdspe_internal_pitch(struct hdspe* hdspe);
/* Reads effective system pitch from the RD_PLL_FREQ register, converting
* to parts per milion relative to the controls registers single speed
* frequency setting. */
extern u32 hdspe_read_system_pitch(struct hdspe* hdspe);
/* Read effective system sample rate from hardware. This may differ
* from 32KHz, 44.1KHz, etc... because of DDS (non-neutral system pitch). */
extern u32 hdspe_read_system_sample_rate(struct hdspe* hdspe);
/* Read current system sample rate from hardware - read_status() helper. */
extern void hdspe_read_sample_rate_status(struct hdspe* hdspe,
struct hdspe_status* status);
/* Set arbitray sample rate in the cards range, writing the control
* register single speed frequency closest to the desired rate,
* the speed mode corresponding with the desired rate, and the pll_freq
* register. Forbids speed mode change if there are processes capturing or
* playing back. Sets the channel map according to the desired speed mode
* if allowed and rate differs from current.
* Returns:
* -EBUSY : forbidden speed mode change.
* 0 : desired rate is same as current.
* 1 : new rate set. */
extern int hdspe_set_sample_rate(struct hdspe * hdspe, u32 desired_rate);
/* Check if same process is writing and reading. */
static inline __attribute__((always_inline))
int snd_hdspe_use_is_exclusive(struct hdspe *hdspe)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&hdspe->lock, flags);
if ((hdspe->playback_pid != hdspe->capture_pid) &&
(hdspe->playback_pid >= 0) && (hdspe->capture_pid >= 0)) {
ret = 0;
}
spin_unlock_irqrestore(&hdspe->lock, flags);
return ret;
}
/* read_status() helper. */
static inline __attribute__((always_inline))
void hdspe_set_sync_source(struct hdspe_status* status,
int i,
enum hdspe_freq freq,
bool lock, bool sync, bool present)
{
status->freq[i] = freq;
status->sync[i] = HDSPE_MAKE_SYNC_STATUS(lock, sync, present);
}
/**
* hdspe_madi.c
*/
extern int hdspe_init_madi(struct hdspe* hdspe);
extern void hdspe_terminate_madi(struct hdspe* hdspe);
extern enum hdspe_freq hdspe_madi_get_external_freq(struct hdspe* hdspe);
/**
* hdspe_aes.c
*/
extern int hdspe_init_aes(struct hdspe* hdspe);
extern void hdspe_terminate_aes(struct hdspe* hdspe);
/**
* hdspe_raio.c
*/
extern int hdspe_init_raio(struct hdspe* hdspe);
extern void hdspe_terminate_raio(struct hdspe* hdspe);
#endif /* __SOUND_HDSPE_CORE_H */
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_hwdep.c 0000664 0000000 0000000 00000017110 15060370513 0022001 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* hdspe_hwdep.c
* @brief RME HDSPe driver HWDEP interface.
*
* 20210728 - Philippe.Bekaert@uhasselt.be
* 20210810,12 - PhB : new card info ioctl.
* 20211125 - PhB : IOCTL_GET_CONFIG reimplemented in terms of hdspe_status.
*
* Refactored work of the other MODULE_AUTHORs.
*/
#include "hdspe.h"
#include "hdspe_core.h"
#include
#ifdef OLDSTUFF
/* AutoSync external sync source frequency class. Returns 0 if
* no valid external reference. */
static enum hdspe_freq hdspe_autosync_freq(struct hdspe *hdspe)
{
struct hdspe_status status;
hdspe->m.read_status(hdspe, &status);
return status.external_freq;
}
#endif /*OLDSTUFF*/
static int snd_hdspe_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
{
/* we have nothing to initialize but the call is required */
return 0;
}
static inline int copy_u32_le(void __user *dest, void __iomem *src)
{
u32 val = readl(src);
return copy_to_user(dest, &val, 4);
}
void hdspe_get_card_info(struct hdspe* hdspe, struct hdspe_card_info *s)
{
s->version = HDSPE_VERSION;
s->card_type = hdspe->io_type;
s->serial = hdspe->serial;
s->fw_rev = hdspe->firmware_rev;
s->fw_build = hdspe->fw_build;
s->irq = hdspe->irq;
s->port = hdspe->port;
s->vendor_id = hdspe->vendor_id;
s->expansion = 0;
if (hdspe->tco)
s->expansion |= HDSPE_EXPANSION_TCO;
}
static int snd_hdspe_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
struct hdspe *hdspe = hw->private_data;
struct hdspe_mixer_ioctl mixer;
struct hdspe_config info;
struct hdspe_version hdspe_version;
struct hdspe_peak_rms *levels;
#ifdef OLDSTUFF
struct hdspe_ltc ltc;
#endif /*OLDSTUFF*/
struct hdspe_status status;
struct hdspe_card_info card_info;
struct hdspe_tco_status tco_status;
long unsigned int s;
int i = 0;
switch (cmd) {
case SNDRV_HDSPE_IOCTL_GET_CARD_INFO:
hdspe->m.get_card_info(hdspe, &card_info);
if (copy_to_user(argp, &card_info,
sizeof(struct hdspe_card_info)))
return -EFAULT;
break;
case SNDRV_HDSPE_IOCTL_GET_STATUS:
hdspe->m.read_status(hdspe, &status);
if (copy_to_user(argp, &status, sizeof(struct hdspe_status)))
return -EFAULT;
break;
case SNDRV_HDSPE_IOCTL_GET_LTC:
if (!hdspe->tco) {
dev_dbg(hdspe->card->dev, "%s: %d: EINVAL\n", __func__, __LINE__);
return -EINVAL;
} else {
hdspe_tco_read_status(hdspe, &tco_status);
}
if (copy_to_user(argp, &tco_status,
sizeof(struct hdspe_tco_status)))
return -EFAULT;
break;
case SNDRV_HDSPE_IOCTL_GET_PEAK_RMS:
levels = &hdspe->peak_rms;
for (i = 0; i < HDSPE_MAX_CHANNELS; i++) {
levels->input_peaks[i] =
readl(hdspe->iobase +
HDSPE_MADI_INPUT_PEAK + i*4);
levels->playback_peaks[i] =
readl(hdspe->iobase +
HDSPE_MADI_PLAYBACK_PEAK + i*4);
levels->output_peaks[i] =
readl(hdspe->iobase +
HDSPE_MADI_OUTPUT_PEAK + i*4);
levels->input_rms[i] =
((uint64_t) readl(hdspe->iobase +
HDSPE_MADI_INPUT_RMS_H + i*4) << 32) |
(uint64_t) readl(hdspe->iobase +
HDSPE_MADI_INPUT_RMS_L + i*4);
levels->playback_rms[i] =
((uint64_t)readl(hdspe->iobase +
HDSPE_MADI_PLAYBACK_RMS_H+i*4) << 32) |
(uint64_t)readl(hdspe->iobase +
HDSPE_MADI_PLAYBACK_RMS_L + i*4);
levels->output_rms[i] =
((uint64_t)readl(hdspe->iobase +
HDSPE_MADI_OUTPUT_RMS_H + i*4) << 32) |
(uint64_t)readl(hdspe->iobase +
HDSPE_MADI_OUTPUT_RMS_L + i*4);
}
levels->speed = hdspe_speed_mode(hdspe);
levels->status2 = hdspe_read_status2(hdspe).raw;
s = copy_to_user(argp, levels, sizeof(*levels));
if (0 != s) {
/* dev_err(hdspe->card->dev, "copy_to_user(.., .., %lu): %lu
[Levels]\n", sizeof(struct hdspe_peak_rms), s);
*/
return -EFAULT;
}
break;
#ifdef OLDSTUFF
case SNDRV_HDSPE_IOCTL_GET_LTC:
ltc.ltc = hdspe_read(hdspe, HDSPE_RD_TCO);
i = hdspe_read(hdspe, HDSPE_RD_TCO + 4);
if (i & HDSPE_TCO1_LTC_Input_valid) {
switch (i & (HDSPE_TCO1_LTC_Format_LSB |
HDSPE_TCO1_LTC_Format_MSB)) {
case 0:
ltc.format = fps_24;
break;
case HDSPE_TCO1_LTC_Format_LSB:
ltc.format = fps_25;
break;
case HDSPE_TCO1_LTC_Format_MSB:
ltc.format = fps_2997;
break;
default:
ltc.format = fps_30;
break;
}
if (i & HDSPE_TCO1_set_drop_frame_flag) {
ltc.frame = drop_frame;
} else {
ltc.frame = full_frame;
}
} else {
ltc.format = format_invalid;
ltc.frame = frame_invalid;
}
if (i & HDSPE_TCO1_Video_Input_Format_NTSC) {
ltc.input_format = ntsc;
} else if (i & HDSPE_TCO1_Video_Input_Format_PAL) {
ltc.input_format = pal;
} else {
ltc.input_format = no_video;
}
s = copy_to_user(argp, <c, sizeof(ltc));
if (0 != s) {
/*
dev_err(hdspe->card->dev, "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspe_ltc), s); */
return -EFAULT;
}
#endif /*OLDSTUFF*/
dev_warn(hdspe->card->dev, "IOCTL_GET_LTC: TBD.\n");
break;
case SNDRV_HDSPE_IOCTL_GET_CONFIG:
memset(&info, 0, sizeof(info));
spin_lock_irq(&hdspe->lock);
hdspe->m.read_status(hdspe, &status);
info.pref_sync_ref = status.preferred_ref;
info.wordclock_sync_check =
status.sync[HDSPE_CLOCK_SOURCE_WORD];
snd_BUG_ON(status.sample_rate_denominator == 0);
info.system_sample_rate = div_u64(status.sample_rate_numerator,
status.sample_rate_denominator);
info.autosync_sample_rate =
hdspe_freq_sample_rate(status.external_freq);
info.system_clock_mode = status.clock_mode;
info.clock_source = status.internal_freq;
info.autosync_ref = status.autosync_ref;
info.line_out = hdspe->reg.control.common.LineOut;
info.passthru = 0;
#ifdef OLDSTUFF
info.pref_sync_ref = hdspe->m.get_pref_sync_ref(hdspe);
info.wordclock_sync_check = hdspe->m.get_sync_status(
hdspe, HDSPE_CLOCK_SOURCE_WORD);
info.system_sample_rate = hdspe_read_system_sample_rate(hdspe);
info.autosync_sample_rate = hdspe_freq_sample_rate(
hdspe_autosync_freq(hdspe));
info.system_clock_mode = hdspe->m.get_clock_mode(hdspe);
info.clock_source = hdspe_internal_freq(hdspe);
info.autosync_ref = hdspe->m.get_autosync_ref(hdspe);
info.line_out = hdspe->reg.control.common.LineOut;
info.passthru = 0;
#endif /*OLDSTUFF*/
spin_unlock_irq(&hdspe->lock);
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
break;
case SNDRV_HDSPE_IOCTL_GET_VERSION:
memset(&hdspe_version, 0, sizeof(hdspe_version));
hdspe_version.card_type = hdspe->io_type;
strscpy(hdspe_version.cardname, hdspe->card_name,
sizeof(hdspe_version.cardname));
hdspe_version.serial = hdspe->serial;
hdspe_version.firmware_rev = hdspe->firmware_rev;
hdspe_version.addons = 0;
if (hdspe->tco)
hdspe_version.addons |= HDSPE_ADDON_TCO;
if (copy_to_user(argp, &hdspe_version,
sizeof(hdspe_version)))
return -EFAULT;
break;
case SNDRV_HDSPE_IOCTL_GET_MIXER:
if (copy_from_user(&mixer, argp, sizeof(mixer)))
return -EFAULT;
if (copy_to_user((void __user *)mixer.mixer, hdspe->mixer,
sizeof(*mixer.mixer)))
return -EFAULT;
break;
default:
dev_dbg(hdspe->card->dev, "%s: %d: cmd=%u EINVAL\n", __func__, __LINE__, cmd);
return -EINVAL;
}
return 0;
}
int snd_hdspe_create_hwdep(struct snd_card *card,
struct hdspe *hdspe)
{
struct snd_hwdep *hw;
int err;
err = snd_hwdep_new(card, "HDSPE hwdep", 0, &hw);
if (err < 0)
return err;
hdspe->hwdep = hw;
hw->private_data = hdspe;
strcpy(hw->name, "HDSPE hwdep interface");
hw->ops.open = snd_hdspe_hwdep_dummy_op;
hw->ops.ioctl = snd_hdspe_hwdep_ioctl;
hw->ops.ioctl_compat = snd_hdspe_hwdep_ioctl;
hw->ops.release = snd_hdspe_hwdep_dummy_op;
return 0;
}
snd-hdspe-1.0.2/sound/pci/hdsp/hdspe/hdspe_ltc_math.c 0000664 0000000 0000000 00000024150 15060370513 0022467 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: GPL-2.0-or-later
/**
* hdspe_ltc_math.c
* @brief RME HDSPe 32-bit LTC code optimised math for TCO module.
*
* 20210930,1001,08 - Philippe.Bekaert@uhasselt.be
*/
#include "hdspe_ltc_math.h"
int hdspe_ltc_fpd(int fps, int df)
{
return df ? 24*107892 : 24*60*60*fps;
}
void hdspe_ltc32_parse(u32 ltc, int *h, int *m, int *s, int *f)
{
*h = ((ltc >> 28) & 0x03)*10 + ((ltc >> 24) & 0x0f);
*m = ((ltc >> 20) & 0x07)*10 + ((ltc >> 16) & 0x0f);
*s = ((ltc >> 12) & 0x07)*10 + ((ltc >> 8) & 0x0f);
*f = ((ltc >> 4) & 0x03)*10 + ((ltc >> 0) & 0x0f);
}
u32 hdspe_ltc32_compose(int h, int m, int s, int f)
{
int H = h/10;
int M = m/10;
int S = s/10;
int F = f/10;
h -= H*10;
m -= M*10;
s -= S*10;
f -= F*10;
return ((H&0x03)<<28)|((h&0x0f)<<24)|((M&0x07)<<20)|((m&0x0f)<<16)|
((S&0x07)<<12)|((s&0x0f)<< 8)|((F&0x03)<< 4)|((f&0x0f) );
}
int hdspe_ltc32_cmp(u32 ltc1, u32 ltc2)
{
return (int)(ltc1 & 0x3f7f7f3f) - (int)(ltc2 & 0x3f7f7f3f);
}
/**
* hdspe_ltc32_to_frames_df: Convert LTC to frames since midnight, for
* drop-frame 32-bit LTC.
* @ltc: 32-bit ltc code to convert to frames since midnight.
*/
static unsigned int hdspe_ltc32_to_frames_df(u32 ltc)
{
return ((ltc >> 28) & 0x3) * 1078920
+ ((ltc >> 24) & 0xf) * 107892
+ ((ltc >> 20) & 0x7) * 17982
+ ((ltc >> 16) & 0xf) * 1798
+ ((ltc >> 12) & 0x7) * 300
+ ((ltc >> 8) & 0xf) * 30
+ ((ltc >> 4) & 0x3) * 10
+ ((ltc ) & 0xf);
}
/**
* hdspe_ltc32_to_frames_ndf: Convert LTC to frames since midnight,
* for non-drop-frame 32-bit LTC.
* @ltc: 32-bit LTC code to convert.
* @fps: LTC frames per second.
*/
static unsigned int hdspe_ltc32_to_frames_ndf(u32 ltc, int fps)
{
return (((ltc >> 28) & 0x3) * 36000
+ ((ltc >> 24) & 0xf) * 3600
+ ((ltc >> 20) & 0x7) * 600
+ ((ltc >> 16) & 0xf) * 60
+ ((ltc >> 12) & 0x7) * 10
+ ((ltc >> 8) & 0xf)) * fps
+ ((ltc >> 4) & 0x3) * 10
+ ((ltc ) & 0xf);
}
unsigned int hdspe_ltc32_to_frames(u32 ltc, int fps, int df)
{
return df ? hdspe_ltc32_to_frames_df(ltc)
: hdspe_ltc32_to_frames_ndf(ltc, fps);
}
u32 hdspe_ltc32_from_frames(int frames, int fps, int df)
{
int H, h, M, m, S, s, F, f;
int fpd = hdspe_ltc_fpd(fps, df);
f = frames % fpd;
if (f < 0) f += fpd;
if (!df) {
s = f / fps;
f -= s * fps;
F = f / 10;
f -= F * 10;
m = s / 60;
s -= m * 60;
S = s / 10;
s -= S * 10;
h = m / 60;
m -= h * 60;
M = m / 10;
m -= M * 10;
H = h / 10;
h -= H * 10;
} else {
H = f / 1078920;
f -= H * 1078920;
h = f / 107892;
f -= h * 107892;
M = f / 17982;
f -= M * 17982;
if (f < 1800) {
m = 0;
S = f / 300;
f -= S * 300;
s = f / 30;
f -= s * 30;
F = f / 10;
f -= F * 10;
} else {
f -= 1800;
m = f / 1798;
f -= m * 1798 - 2;
m ++;
S = f / 300;
f -= S * 300;
s = f / 30;
f -= s * 30;
F = f / 10;
f -= F * 10;
}
}
return ((H&3)<<28)|(h<<24)|(M<<20)|(m<<16)|(S<<12)|(s<<8)|(F<<4)|f;
}
u32 hdspe_ltc32_decr(u32 tci, int fps, int df)
{
u32 tco = 0;
int f = tci & 0xf;
f --;
if (df && f < 2 && (tci & 0x00007ff0) == 0 && (tci & 0x000f0000) != 0) {
f -= 2;
}
if (f < 0) {
int F = (tci >> 4) & 0x3;
F --; f = 9;
if (F < 0) {
int s = (tci >> 8) & 0xf;
s --; F = (fps-1)/10; f = fps-1 - F*10;
if (s < 0) {
int S = (tci >> 12) & 0x7;
S --; s = 9;
if (S < 0) {
int m = (tci >> 16) & 0xf;
m --; S = 5;
if (m < 0) {
int M = (tci >> 20) & 0x7;
M --; m = 9;
if (M < 0) {
int h = (tci >> 24) & 0xf;
h --; M = 5;
if (h < 0) {
int H = (tci >> 28) & 0x3;
H --; h = 9;
if (H < 0) {
H = 2; h = 3;
}
tco = H << 28;
} else {
tco = tci & 0x30000000;
}
tco |= h << 24;
} else {
tco = tci & 0x3f000000;
}
tco |= M << 20;
} else {
tco = tci & 0x3f700000;
}
tco |= m << 16;
} else {
tco = tci & 0x3f7f0000;
}
tco |= S << 12;
} else {
tco = tci & 0x3f7f7000;
}
tco |= s << 8;
} else {
tco = tci & 0x3f7f7f00;
}
tco |= F << 4;
} else {
tco = tci & 0x3f7f7f30;
}
return tco | f;
}
u32 hdspe_ltc32_incr(u32 tci, int fps, int df)
{
int tco = 0;
int f = (tci >> 0) & 0xf;
int F = (tci >> 4) & 0x3;
f ++;
if (f >= 10) {
F ++; f = 0;
}
if (10*F + f >= fps) {
int s = (tci >> 8) & 0xf;
s ++; F = f = 0;
if (s >= 10) {
int S = (tci >> 12) & 0x7;
S ++; s = 0;
if (S >= 6) {
int m = (tci >> 16) & 0xf;
m ++; S = 0;
if (m >= 10) {
int M = (tci >> 20) & 0x7;
M ++; m = 0;
if (M >= 6) {
int h = (tci >> 24) & 0xf;
int H = (tci >> 28) & 0x3;
h ++; M = 0;
if (h >= 10) {
H ++; h = 0;
}
if (10*H + h >= 24) {
H = h = 0;
}
tco |= (H << 28)|(h << 24);
} else {
tco = tci & 0x3f000000;
}
tco |= (M << 20);
} else {
if (df) f = 2;
tco = tci & 0x3f700000;
}
tco |= (m << 16);
} else {
tco = tci & 0x3f7f0000;
}
tco |= (S << 12);
} else {
tco = tci & 0x3f7f7000;
}
tco |= (s << 8);
} else {
tco = tci & 0x3f7f7f00;
}
tco |= (F << 4)|(f << 0);
return tco;
}
int hdspe_ltc32_running(u32 ltc1, u32 ltc2, int fps, int df)
{
if (((ltc1+1) & 0x3f7f7f3f) == (ltc2 & 0x3f7f7f3f) ||
hdspe_ltc32_cmp(hdspe_ltc32_incr(ltc1, fps, df), ltc2) == 0)
return +1;
if (hdspe_ltc32_cmp(hdspe_ltc32_incr(ltc2, fps, df), ltc1) == 0)
return -1;
return 0;
}
u32 hdspe_ltc32_add_frames(int n, u32 ltc, int fps, int df)
{
int frames = hdspe_ltc32_to_frames(ltc, fps, df);
return hdspe_ltc32_from_frames(frames+n, fps, df);
}
unsigned int hdspe_ltc32_diff_frames(u32 ltc1, u32 ltc2, int fps, int df)
{
int frames1 = hdspe_ltc32_to_frames(ltc1, fps, df);
int frames2 = hdspe_ltc32_to_frames(ltc2, fps, df);
int diff = frames1 - frames2;
int fpd = hdspe_ltc_fpd(fps, df);
diff = diff % fpd;
return diff < 0 ? diff + fpd : diff;
}
#ifdef UNIT_TESTING
/////////////////////////////////////////////////////////////////////////////
// Unit testing.
#include
#include
#include
#include
int test_compose_parse(int h, int m, int s, int f, int fps, int df)
{
int h1, m1, s1, f1;
u32 ltc = hdspe_ltc32_compose(h, m, s, f);
hdspe_ltc32_parse(ltc, &h1, &m1, &s1, &f1);
return ltc_cmp(h, m, s, f, h1, m1, s1, f1) == 0;
}
int test_to_from_frames_32(int h, int m, int s, int f, int fps, int df)
{
u32 ltc = hdspe_ltc32_compose(h, m, s, f);
int frames = hdspe_ltc32_to_frames(ltc, fps, df);
u32 ltc1 = hdspe_ltc32_from_frames(frames, fps, df);
return hdspe_ltc32_cmp(ltc, ltc1) == 0;
}
int test_incr_decr_32(int h, int m, int s, int f, int fps, int df)
{
u32 ltc = hdspe_ltc32_compose(h, m, s, f);
int frames = hdspe_ltc32_to_frames(ltc, fps, df);
u32 ltc1 = hdspe_ltc32_incr(ltc, fps, df);
int frames1 = hdspe_ltc32_to_frames(ltc1, fps, df);
if (frames1 != frames+1) {
fprintf(stderr, "%08x %d %sfps (frames=%d) +1 = %08x (frames %d)\n",
ltc, fps, df ? "d" : "", frames,
ltc1, frames1);
}
u32 ltc2 = hdspe_ltc32_add_frames(1, ltc, fps, df);
if (hdspe_ltc32_cmp(ltc1, ltc2) != 0) {
fprintf(stderr, "hdspe_ltc32_add_frames +1: %08x != %08x\n",
ltc2, ltc1);
return 0;
}
ltc2 = hdspe_ltc32_add_frames(-1, ltc2, fps, df);
if (hdspe_ltc32_cmp(ltc, ltc2) != 0) {
fprintf(stderr, "hdspe_ltc32_add_frames -1: %08x - 1 = %08x != %08x\n",
ltc1, ltc2, ltc);
return 0;
}
ltc2 = hdspe_ltc32_decr(ltc1, fps, df);
if (hdspe_ltc32_cmp(ltc, ltc2) != 0) {
fprintf(stderr, "hdspe_ltc32_decr: %08x -> %08x != %08x\n",
ltc1, ltc2, ltc);
return 0;
}
return 1;
}
int test_add_diff_single_32(int n, int h, int m, int s, int f, int fps, int df)
{
u32 ltc = hdspe_ltc32_compose(h, m, s, f);
u32 ltc1 = hdspe_ltc32_add_frames(n, ltc, fps, df);
int diff = hdspe_ltc32_diff_frames(ltc1, ltc, fps, df);
int fpd = hdspe_ltc_fpd(fps, df);
int expectdiff = n >= 0 ? n : n + fpd;
if (diff != expectdiff) {
fprintf(stderr, "hdspe_ltc32_add_frames: %08x + %d = %08x - . = %d != %d.\n",
ltc, n, ltc1, diff, n);
return 0;
}
int running = hdspe_ltc32_running(ltc, ltc1, fps, df);
int expect = (n == 1)||(n == -fpd+1) ? 1 : (n == -1)||(n == fpd-1) ? -1 : 0;
if (running != expect) {
fprintf(stderr, "hdspe_ltc32_running: n=%d, %08x -> %08x = %d != %d.\n",
n, ltc, ltc1, running, expect);
return 0;
}
return 1;
}
int test_add_diff_32(int h, int m, int s, int f, int fps, int df)
{
int n[] = { 0, 1, fps, fps*60, fps*3600,
lrand48() % hdspe_ltc_fpd(fps, df) };
for (int i=0; i