Alsa-devel
Threads by month
- ----- 2024 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
October 2008
- 122 participants
- 253 discussions
Hello all,
I am reporting an issue for last 4 days and i am not getting enough response
fromt he mailing list.
My issue is with the asoc drivers for omap3 and twl4030 combination which
are available in the latest git kernel.
Audio is working on my custom board when i boot out of MMC but the same
setup (x-laod, u-boot and uImage and hadware) the audio is not working when
i boot out of NAND.
Can any one help me in debugging the issue.
Background:
I am using 2.6.26 linux kernel i have ported soc stuff from 2.6.27 latest
git kernel for omap3 mcbsp and twl4030 codec support.. the audio is working
when i use it on OMAP3EVM even though it is booted out of NAND.
I am getting very less replies against my issue am i posting to wrong people
or what
can any one tell me how can i make use of this mailing list to its fullest
--
Thank You all,
Neyo.
3
2
Re: [alsa-devel] [PATCH] ALSA: emu10k1: fix device names for Live!/Audigy1/2/4/E-mu and coding style
by Vedran Miletić 23 Oct '08
by Vedran Miletić 23 Oct '08
23 Oct '08
Do you have the non-working old revision or working new revision?
Because, if it already works, it will continue to work. However, if it
doesn, then it won't change :-( It's a known hardware issue with old
E-mu 0404 (EM8850 if I'm not mistaken).
My code changes are mostly cleanups to adhere to standards and changes
to make device names more user friendly and consistent, no big changes
since I'm not a real programmer (yet! :-) who knows...).
2008/10/22 adrian dezeo <adezeo(a)gmail.com>:
> Hi Vedran:
>
> My name is Adrian Dezeo and I'm from Argentina.
> I have a emu 0404 and gladly will test any code from the emu10k1 alsa
> driver if you need to.
> Thanks in advance.
> best regards.
>
> Adrian.
>
>
> Vedran Miletić wrote:
>>
>> Hmm... I actually followed James here, he set standard of naming with
>> CardBus 1616's name. But, oh well. I can fix it in my next run, and I
>> will likely do it when I play around with porting over complete
>> kx.inf's device list.
>>
>> 2008/10/22 Tony Vroon <tony(a)vroon.org>:
>>
>>>
>>> On a related note, it looks to me as if the leading two characters (the
>>> manufacturing location) have been erroneously included for E-MU
>>> products.
>>> You'll see this MA code on some sound cards as well (MASBxxxx)
>>>
>>> Regards,
>>> Tony V.
>>>
>>>
>>
>>
>>
>>
>
>
--
Vedran Miletić
1
0
[alsa-devel] [PATCH 3/3] saa7146: Emagic Audiowerk8 low-level ALSA driver
by Matthias Nyffenegger 23 Oct '08
by Matthias Nyffenegger 23 Oct '08
23 Oct '08
From: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
Low-level ALSA driver for Emagic Audiowerk8 sound card.
Project page: http://sourceforge.net/projects/aw8-alsa
Built and tested with Vanilla 2.6.25.16
Signed-off-by: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
---
This is a request for submission to ALSA-tree.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/license.txt
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/license.txt
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/license.txt 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/license.txt 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
2
1
[alsa-devel] [PATCH 2/3] saa7146: Emagic Audiowerk8 low-level ALSA driver
by Matthias Nyffenegger 23 Oct '08
by Matthias Nyffenegger 23 Oct '08
23 Oct '08
From: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
Low-level ALSA driver for Emagic Audiowerk8 sound card.
Project page: http://sourceforge.net/projects/aw8-alsa
Built and tested with Vanilla 2.6.25.16
Signed-off-by: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
---
This is a request for submission to ALSA-tree.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1 @@
+See sourceforge.net project "Audiowerk8 ALSA driver" (aw8-alsa) Wiki for more information.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,8 @@
+# Makefile for Audiowerk module.
+# run command in source directory:
+# make -C /usr/src/linux M=`pwd` modules modules_install
+
+EXTRA_CFLAGS := -DSAA7146_SUBSYS_LOG_TAG=\"AW8\"
+obj-m := snd-aw8.o
+snd-aw8-objs := saa7146i2c.o saa7146i2s.o saa7146audio.o aw8.o aw8alsa.o
+
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,826 @@
+/*
+ * Emagic Audiowerk ALSA driver
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "log.h"
+#include "saa7146audio.h"
+#include "aw8.h"
+
+/* From Takashi Iwai's "Writing an ALSA Driver" developer guide */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+/*
+ * Module info
+ */
+MODULE_AUTHOR("Matthias Nyffenegger matthias.nyffenegger[AT]bluewin.ch");
+MODULE_DESCRIPTION(AW_MODULE_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0:0.2");
+
+/**
+ * Module parameter 'input_type': select capture input digital or analog
+ * (default)
+ */
+int input_type;
+module_param(input_type, int, 0444);
+MODULE_PARM_DESC(input_type, "0=analog (default), 1=digital");
+
+#define PCM_0 0
+
+/**
+ * The ALSA 'chip' data structure.
+ */
+struct awalsa_t {
+ /* the alsa related stuff */
+ struct snd_card *card;
+ struct snd_pcm *pcm_0;
+ struct snd_pcm_substream *pcm_in_substreams[MAX_IN_AUDIO_STREAMS];
+ struct snd_pcm_substream *pcm_out_substreams[MAX_OUT_AUDIO_STREAMS];
+ /* the SAA7146A stuff */
+ struct saa7146audio chipaudio;
+ struct audio_stream *in_streams[MAX_IN_AUDIO_STREAMS];
+ struct audio_stream *out_streams[MAX_OUT_AUDIO_STREAMS];
+ /*/ the PCI stuff */
+ struct pci_dev *pci;
+ int irq;
+ /* other useful things */
+ spinlock_t isr_lock;
+};
+
+static int snd_aw_substream_open(struct snd_pcm_substream *substream, int in);
+static int snd_aw_substream_close(struct snd_pcm_substream *substream, int in);
+static int snd_aw_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params);
+static int snd_aw_pcm_hw_free(struct snd_pcm_substream *substream);
+static int snd_aw_pcm_prepare_substream(struct snd_pcm_substream *substream,
+ int in);
+static int snd_aw_pcm_trigger_substream(struct snd_pcm_substream *substream,
+ int cmd, int in);
+static snd_pcm_uframes_t snd_aw_pcm_pointer_substream(
+ struct snd_pcm_substream *substream,
+ int in);
+
+/**
+ *
+ * ALSA capture stream definitions and callbacks
+ *
+ */
+static struct snd_pcm_hardware snd_aw_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE),
+ .rates = AW_PCM_RATE,
+ .rate_min = AW_FS_MIN,
+ .rate_max = AW_FS_MAX,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 16384,
+ .period_bytes_min = 64, /* HW restriction, see SAA7146A spec p.24 */
+ .period_bytes_max = 8192,
+ .periods_min = 2,
+ .periods_max = 2,
+};
+
+/**
+ * ALSA capture stream open callback.
+ */
+static int snd_aw_capture_open(struct snd_pcm_substream *substream)
+{
+ return snd_aw_substream_open(substream, 1);
+}
+
+/**
+ * ALSA capture stream close callback.
+ */
+static int snd_aw_capture_close(struct snd_pcm_substream *substream)
+{
+ return snd_aw_substream_close(substream, 1);
+}
+
+/**
+ * Module scoped helper: converts alsa to Audiowerk capture sample format.
+ */
+static enum aw_formats alsa_2_aw_capture_format(snd_pcm_format_t fmt)
+{
+ switch (fmt) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ return fmt_s16_2le;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ return fmt_s16_2be;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ return fmt_s18_4le;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ return fmt_s18_4be;
+ default:
+ LOG_ERROR("Unsupported capture format %d", fmt);
+ return fmt_s16_2le;
+ }
+}
+
+/**
+ * ALSA capture stream prepare callback.
+ * Be careful that this callback will be called many times at each set up.
+ */
+static int snd_aw_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+ return snd_aw_pcm_prepare_substream(substream, 1);
+}
+
+/**
+ * ALSA capture stream trigger callback. [atomic].
+ */
+static int snd_aw_pcm_trigger_capture(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ return snd_aw_pcm_trigger_substream(substream, cmd, 1);
+}
+
+/**
+ * ALSA capture stream hw-buffer pointer callback. [atomic]
+ */
+static snd_pcm_uframes_t snd_aw_pcm_pointer_capture(
+ struct snd_pcm_substream *substream)
+{
+ return snd_aw_pcm_pointer_substream(substream, 1);
+}
+
+/**
+ * ALSA capture stream callback operations.
+ */
+static struct snd_pcm_ops snd_aw_capture_ops = {
+ .open = snd_aw_capture_open,
+ .close = snd_aw_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_aw_pcm_hw_params,
+ .hw_free = snd_aw_pcm_hw_free,
+ .prepare = snd_aw_pcm_prepare_capture,
+ .trigger = snd_aw_pcm_trigger_capture,
+ .pointer = snd_aw_pcm_pointer_capture,
+};
+
+/**
+ *
+ * ALSA playback stream definitions and callbacks
+ *
+ */
+static struct snd_pcm_hardware snd_aw_playback_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE),
+ .rates = AW_PCM_RATE,
+ .rate_min = AW_FS_MIN,
+ .rate_max = AW_FS_MAX,
+ .channels_min = 2,
+ .channels_max = AW_MAX_PLAYBACK_CHANNELS_PER_STREAM,
+ .buffer_bytes_max = 16384,
+ .period_bytes_min = 64, /* HW restriction, see SAA7146A spec p.24 */
+ .period_bytes_max = 8192,
+ .periods_min = 2,
+ .periods_max = 2,
+};
+
+/**
+ * ALSA playback stream open callback.
+ */
+static int snd_aw_playback_open(struct snd_pcm_substream *substream)
+{
+ return snd_aw_substream_open(substream, 0);
+}
+
+/**
+ * ALSA playback stream close callback.
+ */
+static int snd_aw_playback_close(struct snd_pcm_substream *substream)
+{
+ return snd_aw_substream_close(substream, 0);
+}
+
+/**
+ * Module scoped helper: converts alsa to Audiowerk playback sample format.
+ */
+static enum aw_formats alsa_2_aw_playback_format(snd_pcm_format_t fmt)
+{
+ switch (fmt) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ return fmt_s16_2le;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ return fmt_s16_2be;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ return fmt_s20_4le;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ return fmt_s20_4be;
+ default:
+ LOG_ERROR("Unsupported playback format %d", fmt);
+ return fmt_s16_2le;
+ }
+}
+
+/**
+ * ALSA playback stream prepare callback.
+ * Be careful that this callback will be called many times at each set up.
+ */
+static int snd_aw_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+ return snd_aw_pcm_prepare_substream(substream, 0);
+}
+
+/**
+ * ALSA playback stream trigger callback. [atomic].
+ */
+static int snd_aw_pcm_trigger_playback(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ return snd_aw_pcm_trigger_substream(substream, cmd, 0);
+}
+
+/**
+ * ALSA playback stream hw-buffer pointer callback. [atomic].
+ */
+static snd_pcm_uframes_t snd_aw_pcm_pointer_playback(
+ struct snd_pcm_substream *substream)
+{
+ return snd_aw_pcm_pointer_substream(substream, 0);
+}
+
+/**
+ * ALSA playback stream callback operations.
+ */
+static struct snd_pcm_ops snd_aw_playback_ops = {
+ .open = snd_aw_playback_open,
+ .close = snd_aw_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_aw_pcm_hw_params,
+ .hw_free = snd_aw_pcm_hw_free,
+ .prepare = snd_aw_pcm_prepare_playback,
+ .trigger = snd_aw_pcm_trigger_playback,
+ .pointer = snd_aw_pcm_pointer_playback,
+};
+
+static int snd_aw_substream_open(struct snd_pcm_substream *substream, int in)
+{
+ struct awalsa_t *awalsa = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int device = substream->pcm->device;
+ struct snd_pcm_substream **alsa_pcm_substreams = NULL;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ alsa_pcm_substreams =
+ (in ? awalsa->pcm_in_substreams : awalsa->pcm_out_substreams);
+ if (alsa_pcm_substreams[substream->number] != NULL) {
+ LOG_WARN("%s substream %d already open",
+ (in ? "Capture" : "Playback"), substream->number);
+ return -EAGAIN;
+ }
+ /* Remember substream for interrupt handler where we are given the chip
+ instance only. */
+ alsa_pcm_substreams[substream->number] = substream;
+ runtime->hw = (in ? snd_aw_capture_hw : snd_aw_playback_hw);
+ return 0;
+}
+
+static int snd_aw_substream_close(struct snd_pcm_substream *substream, int in)
+{
+ struct awalsa_t *awalsa = snd_pcm_substream_chip(substream);
+ int device = substream->pcm->device;
+ struct snd_pcm_substream **alsa_pcm_substreams = NULL;
+ struct audio_stream **saa7146_streams = NULL;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ alsa_pcm_substreams =
+ (in ? awalsa->pcm_in_substreams : awalsa->pcm_out_substreams);
+ saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams);
+ alsa_pcm_substreams[substream->number] = NULL;
+ if (saa7146_streams[substream->number] != NULL) {
+ saa7146_stream_unprepare(&awalsa->chipaudio,
+ saa7146_streams[substream->number]);
+ }
+ return 0;
+}
+
+/**
+ * ALSA stream hw_params callback.
+ */
+static int snd_aw_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int err = 0;
+ int device = substream->pcm->device;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0) {
+ LOG_ERROR("snd_pcm_lib_malloc_pages ERROR=%d", err);
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * ALSA stream hw_free callback.
+ */
+static int snd_aw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ int err = 0;
+ int device = substream->pcm->device;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ err = snd_pcm_lib_free_pages(substream);
+ if (err < 0) {
+ LOG_ERROR("snd_pcm_lib_free_pages ERROR=%d", err);
+ return err;
+ }
+ return 0;
+}
+
+static int snd_aw_pcm_prepare_substream(struct snd_pcm_substream *substream,
+ int in)
+{
+ struct awalsa_t *awalsa = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream **saa7146_streams = NULL;
+ struct audio_stream *stream = NULL;
+ int device = substream->pcm->device;
+ enum aw_formats fmt = fmt_s16_2le;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ fmt = (in ? alsa_2_aw_capture_format(runtime->format) :
+ alsa_2_aw_playback_format(runtime->format));
+ saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams);
+ /* as this function may be called many times we must clean up first */
+ if (saa7146_streams[substream->number] != NULL) {
+ saa7146_stream_unprepare(&awalsa->chipaudio,
+ saa7146_streams[substream->number]);
+ }
+ /* try to prepare a new stream with the given parameters */
+ if (in) {
+ stream = saa7146_stream_prepare_capture(&awalsa->chipaudio,
+ substream->number,
+ runtime->dma_addr, /* stream buffer phys. base addr. */
+ 2 * snd_pcm_lib_period_bytes(substream), /* buf size */
+ runtime->channels,
+ ((fmt == fmt_s16_2le || fmt == fmt_s16_2be) ? 2 : 4),
+ ((fmt == fmt_s16_2le || fmt == fmt_s18_4le) ? le : be));
+ } else {
+ stream = saa7146_stream_prepare_playback(&awalsa->chipaudio,
+ substream->number,
+ runtime->dma_addr, /* stream buffer phys. base addr */
+ 2 * snd_pcm_lib_period_bytes(substream), /* buf size */
+ runtime->channels,
+ ((fmt == fmt_s16_2le || fmt == fmt_s16_2be) ? 2 : 4),
+ ((fmt == fmt_s16_2le || fmt == fmt_s20_4le) ? le : be));
+ }
+ if (stream == NULL)
+ return -EINVAL;
+ saa7146_streams[substream->number] = stream;
+ if (aw8_set_samplerate(&awalsa->chipaudio.chipi2s.chip, runtime->rate)
+ != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int snd_aw_pcm_trigger_substream(struct snd_pcm_substream *substream,
+ int cmd, int in)
+{
+ struct awalsa_t *awalsa = snd_pcm_substream_chip(substream);
+ int device = substream->pcm->device;
+ struct audio_stream **saa7146_streams = NULL;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ saa7146_stream_start(&awalsa->chipaudio,
+ saa7146_streams[substream->number]);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ saa7146_stream_stop(&awalsa->chipaudio,
+ saa7146_streams[substream->number]);
+ break;
+ default:
+ LOG_ERROR("Unknown trigger command=%d", cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_aw_pcm_pointer_substream(
+ struct snd_pcm_substream *substream,
+ int in)
+{
+ struct awalsa_t *awalsa = snd_pcm_substream_chip(substream);
+ struct audio_stream **saa7146_streams = NULL;
+ unsigned long hw_ptr = 0;
+ snd_pcm_uframes_t frames;
+ int device = substream->pcm->device;
+
+ if (device != PCM_0) {
+ LOG_ERROR("Invalid device index=%d", device);
+ return -EINVAL;
+ }
+ saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams);
+ hw_ptr = saa7146_stream_get_hw_pointer(&awalsa->chipaudio,
+ saa7146_streams[substream->number]);
+ frames = bytes_to_frames(substream->runtime,
+ hw_ptr - (size_t)(substream->runtime->dma_addr));
+ return frames;
+}
+
+/**
+ *
+ * ALSA device initialization
+ *
+ */
+
+/**
+ * ALSA Setup 1 PCM device:
+ * AW8 substreams: 1 capture /2 playback (1 analog/digital, 1 analog)
+ * AW2 substreams: 1 capture /2 playback (1 analog, 1 digital)
+ */
+static int __devinit snd_aw_new_pcm(struct awalsa_t *awalsa)
+{
+ struct snd_pcm *pcm;
+ int err;
+ int pages = 0;
+
+ /* create the pcm device */
+ err = snd_pcm_new(awalsa->card, "SAA7146A", /* device ID string */
+ PCM_0, /* device index */
+ 2, /* playback substreams AW2/8 */
+ 1, /* capture substreams AW2/8 */
+ &pcm);
+ if (err != 0) {
+ LOG_ERROR("snd_pcm_new ERROR=%d", err);
+ return err;
+ }
+ pcm->private_data = awalsa;
+ strcpy(pcm->name, "Multichannel Capture/Playback");
+ awalsa->pcm_0 = pcm;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_aw_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_aw_capture_ops);
+ LOG_INFO("PCM device [Multichannel Capture/Playback]"
+ " successfully created");
+ /* Pre-allocation 128k of dma buffer */
+ pages = 1 << ((17 - PAGE_SHIFT > 0) ? 17 - PAGE_SHIFT : 0);
+ err = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(awalsa->pci),
+ pages * PAGE_SIZE,
+ pages * PAGE_SIZE);
+ if (err != 0) {
+ LOG_ERROR("snd_pcm_lib_preallocate_pages_for_all"
+ " ERROR=%d", err);
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * ALSA Interrupt Service Routine.
+ */
+static irqreturn_t snd_aw_interrupt(int irq, void *dev_id)
+{
+ struct awalsa_t *awalsa = dev_id;
+ uint32_t isr = 0;
+
+ /* lock isr - block other interrupts */
+ spin_lock(awalsa->isr_lock);
+ isr = saa7146_read(&awalsa->chipaudio.chipi2s.chip, ISR);
+ /* was it one of SAA7146A (remember we might share irq line ..) */
+ if (!isr) {
+ spin_unlock(awalsa->isr_lock);
+ return IRQ_NONE;
+ }
+ /* clear all interrupts */
+ saa7146_write(&awalsa->chipaudio.chipi2s.chip, ISR, isr);
+ if (isr & A1_in && awalsa->pcm_in_substreams[0] != NULL)
+ snd_pcm_period_elapsed(awalsa->pcm_in_substreams[0]);
+ if (isr & A1_out && awalsa->pcm_out_substreams[0] != NULL)
+ snd_pcm_period_elapsed(awalsa->pcm_out_substreams[0]);
+ if (isr & A2_out && awalsa->pcm_out_substreams[1] != NULL)
+ snd_pcm_period_elapsed(awalsa->pcm_out_substreams[1]);
+ spin_unlock(awalsa->isr_lock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ALSA chip-specific destructor.
+ * (see "PCI Resource Managements")
+ */
+static int snd_aw_free(struct awalsa_t *awalsa)
+{
+ struct saa7146i2s *chipi2s = &awalsa->chipaudio.chipi2s;
+
+ /* disable hardware here if any */
+ aw_exit(chipi2s);
+ if (awalsa->irq >= 0)
+ free_irq(awalsa->irq, (void *)awalsa);
+ if (chipi2s->chip.iobase_virt)
+ iounmap(chipi2s->chip.iobase_virt);
+ pci_release_regions(awalsa->pci);
+ pci_disable_device(awalsa->pci);
+ kfree(awalsa);
+ return 0;
+}
+
+/**
+ * ALSA component-destructor.
+ * (see "Management of Cards and Components")
+ */
+static int snd_aw_dev_free(struct snd_device *device)
+{
+ snd_aw_free(device->device_data);
+ return 0;
+}
+
+/**
+ * ALSA chip-specific constructor.
+ * (see "Management of Cards and Components")
+ */
+static int __devinit snd_aw_create(struct snd_card *card,
+ struct pci_dev *pci,
+ struct awalsa_t **rchip)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = snd_aw_dev_free,
+ };
+ int err;
+ struct awalsa_t *awalsa;
+ *rchip = NULL;
+
+ /* Initialize the PCI entry: This function switches the device 'on'.
+ In some cases, IRQ and I/O range is assigned. */
+ err = pci_enable_device(pci);
+ if (err != 0) {
+ LOG_ERROR("pci_enable_device ERROR=%d", err);
+ return err;
+ }
+ pci_set_master(pci);
+ err = pci_set_dma_mask(pci, DMA_32BIT_MASK);
+ if (err != 0) {
+ LOG_ERROR("pci_set_dma_mask ERROR=%d", err);
+ goto snd_aw_create_exit0;
+ }
+ err = pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK);
+ if (err != 0) {
+ LOG_ERROR("pci_set_consistent_dma_mask ERROR=%d", err);
+ goto snd_aw_create_exit0;
+ }
+ awalsa = kzalloc(sizeof(*awalsa), GFP_KERNEL);
+ if (awalsa == NULL) {
+ LOG_ERROR("Could not allocate memory");
+ err = -ENOMEM;
+ goto snd_aw_create_exit0;
+ }
+ awalsa->card = card;
+ awalsa->pci = pci;
+ awalsa->irq = -1;
+ spin_lock_init(&awalsa->isr_lock);
+ /* (1) PCI resource allocation */
+ err = pci_request_regions(pci, "SAA7146A");
+ if (err != 0) {
+ LOG_ERROR("pci_request_regions ERROR=%d", err);
+ goto snd_aw_create_exit1;
+ }
+ /* Get the device's registers physical base address. The device register
+ space is not available for access by the driver now; it has to be
+ mapped to a virtual memory space first. */
+ awalsa->chipaudio.chipi2s.chip.iobase_phys = pci_resource_start(pci, 0);
+ awalsa->chipaudio.chipi2s.chip.iolen = pci_resource_len(pci, 0);
+ /* Map device registers to virtual memory. 'nocache' means that we don't
+ want the device's registers to be read-cached by the CPU (e.g. as a
+ result of compiler optimizations). This is important since the CPU
+ does not necessarily know when a register value has changed (unless
+ this was by a write action of the driver). */
+ awalsa->chipaudio.chipi2s.chip.iobase_virt =
+ ioremap_nocache(awalsa->chipaudio.chipi2s.chip.iobase_phys,
+ awalsa->chipaudio.chipi2s.chip.iolen);
+ err = request_irq(pci->irq, snd_aw_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ "SAA7146A", (void *)awalsa);
+ if (err != 0) {
+ LOG_ERROR("Cannot grab irq %d, ERROR=%d", pci->irq, err);
+ err = -EBUSY;
+ goto snd_aw_create_exit2;
+ }
+ awalsa->irq = pci->irq;
+ /* (2) initialization of the chip hardware */
+ if (aw_init(&awalsa->chipaudio.chipi2s, input_type) != 0)
+ goto snd_aw_create_exit2;
+ /* (3) create alsa device */
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, awalsa, &ops);
+ if (err < 0) {
+ LOG_ERROR("snd_device_new ERROR=%d", err);
+ goto snd_aw_create_exit2;
+ }
+ snd_card_set_dev(card, &pci->dev);
+ *rchip = awalsa;
+ return 0;
+snd_aw_create_exit2:
+ snd_aw_free(awalsa);
+ return err;
+snd_aw_create_exit1:
+ kfree(awalsa);
+snd_aw_create_exit0:
+ pci_disable_device(pci);
+ return err;
+}
+
+/**
+ * ALSA device constructor -- see "Constructor" sub-section in Takashi Iwai's
+ * "Writing an ALSA Driver" developer guide.
+ */
+static int __devinit snd_aw_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ int err;
+ struct snd_card *card;
+ struct awalsa_t *awalsa;
+
+ /* (1) */
+ if (dev >= SNDRV_CARDS) {
+ LOG_ERROR("No device available ERROR=%d", -ENODEV);
+ return -ENODEV;
+ }
+ if (!enable[dev]) {
+ dev++;
+ LOG_ERROR("No device entry ERROR=%d", -ENOENT);
+ return -ENOENT;
+ }
+ /* (2) */
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL) {
+ LOG_ERROR("snd_card_new ERROR=%d", -ENOMEM);
+ return -ENOMEM;
+ }
+ /* (3) */
+ err = snd_aw_create(card, pci, &awalsa);
+ if (err < 0) {
+ LOG_ERROR("snd_aw_create ERROR=%d", err);
+ goto snd_aw_probe_exit0;
+ }
+ /* (4) */
+ strcpy(card->driver, "SAA7146A");
+ strcpy(card->shortname, AW_CARD_SHORT_NAME);
+ sprintf(card->longname,
+ "%s at 0x%lx [%i] irq %i",
+ card->shortname,
+ awalsa->chipaudio.chipi2s.chip.iobase_phys,
+ awalsa->chipaudio.chipi2s.chip.iolen,
+ awalsa->irq);
+ LOG_INFO("card-info[%s]", card->longname);
+ /* (5) */
+ err = snd_aw_new_pcm(awalsa);
+ if (err != 0) {
+ LOG_ERROR("snd_aw_new_pcm ERROR=%d", err);
+ goto snd_aw_probe_exit0;
+ }
+ /* (6) */
+ err = snd_card_register(card);
+ if (err < 0) {
+ LOG_ERROR("snd_card_register ERROR=%d", err);
+ goto snd_aw_probe_exit0;
+ }
+ /* (7) */
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+snd_aw_probe_exit0:
+ snd_card_free(card);
+ return err;
+}
+
+/**
+ * ALSA device destructor -- see "Destructor" sub-section
+ */
+static void __devexit snd_aw_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/**
+ *
+ * PCI specific stuff
+ *
+ */
+
+/**
+ * Organisation of EEPROM of the SAA7146A:
+ * The data of the subsystem ID and the subsystem vendor ID is organized in
+ * the EEPROM in the following order:
+ *
+ * EE-Addr Value Configuration Space Addr.
+ * ---------------------------------------------------------------------
+ * 00 Subsys ID (high byte) 2C, bits: 31 - 24
+ * 01 Subsys ID (low byte) 2C, bits: 23 - 16
+ * 02 Subsys Vendor ID (high byte) 2C, bits: 15 - 8
+ * 03 Subsys Vendor ID (low byte) 2C, bits: 7 - 0
+ * 04 Max_Lat 3C, bits: 31 - 24
+ * 05 Min_Gnt 3C, bits: 23 - 16
+ *
+ * For AW8 Subsys ID and Subsys Vendor ID are 0xFF on my card.
+ * For AW2 Subsys ID and Subsys Vendor ID are 0x00 on my card.
+ */
+
+static struct pci_device_id snd_aw_ids[] = {
+ { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_aw_ids);
+
+/**
+ * pci_driver definition.
+ */
+static struct pci_driver driver = {
+ .name = AW_DRIVER_NAME,
+ .id_table = snd_aw_ids,
+ .probe = snd_aw_probe,
+ .remove = __devexit_p(snd_aw_remove),
+};
+
+/**
+ * initialization of the module.
+ */
+static int __init alsa_card_aw_init(void)
+{
+ int err;
+
+ err = pci_register_driver(&driver);
+ if (err != 0) {
+ LOG_ERROR("pci_register_driver ERROR=%d", err);
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * clean up the module.
+ */
+static void __exit alsa_card_aw_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_aw_init)
+module_exit(alsa_card_aw_exit)
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,185 @@
+/*
+ * Emagic Audiowerk specific functions
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ * Based on GPLed Emagic Audiowerk8 Windows driver provided by Martijn Sipkema.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include "log.h"
+#include "saa7146.h"
+#include "saa7146i2s.h"
+#include "saa7146i2c.h"
+#include "aw8.h"
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define SLEEP_DIGIN 100 /* wait [ms] after switching input type adc/daio */
+#define GPIO_0 GPIO_CTRL
+#define MASK_GPIO_HI 0x50
+#define MASK_GPIO_LO 0x40
+#define SLEEP_PLL 500 /* wait [ms] after setting new fs */
+#define PLL_DEVICE_ADDR 0x62 /* I2C device address of AW8 PLL chip */
+#define PLL_FREF 1000 /* reference frequency for PLL */
+
+/**
+ * TODO: Description
+ */
+enum aw_input_types {analog, digital};
+
+/* forward declarations */
+static int aw8_device_init(struct saa7146i2s *chipi2s);
+static void switch_input(struct saa7146reg *chip, enum aw_input_types type);
+
+/**
+ * see aw8.h
+ */
+int aw_init(struct saa7146i2s *chipi2s, int input_type)
+{
+ int err = 0;
+
+ saa7146_write(&chipi2s->chip, IER, 0); /* disable all interrupts */
+ saa7146_write(&chipi2s->chip, MC1, MRST_N<<16); /* reset SAA7146 */
+ /* setup PCI arbitration ctrl (see SAA7146 spec. page 31 for details):
+ o 8DW burst for all FIFOs
+ o 8DW (empty DWs in FIFO) threshold for OUT-FIFOs
+ o 8DW (valid DWs in FIFO) threshold for IN-FIFOs
+ 0x0e0e0e0e => xxx0 1110 xxx0 1110 xxx0 1110 xxx0 1110 */
+ saa7146_write(&chipi2s->chip, PCI_BT_A, 0x0e0e0e0e);
+ /* enable SAA7146 audio pins */
+ saa7146_write(&chipi2s->chip, MC1, (EAP<<16 | EAP));
+ /* setup audio clock source (from EMagic GPL driver) */
+ saa7146_write(&chipi2s->chip, ACON2, ((0UL<<27) | /* A1_CLKSRC: BCLK1 */
+ (1UL<<22) | /* A2_CLKSRC: BCLK1 */
+ (0UL<<21) | /* INVERT_BCLK1 */
+ (0UL<<20) | /* INVERT_BCLK2 */
+ (1UL<<19) | /* BCLK1_OEN: input */
+ (0UL<<18))); /* BCLK2_OEN: output */
+ /* init ACON1: WS1-4 to output active low */
+ chipi2s->acon1 = 0x0000cccc;
+ saa7146_write(&chipi2s->chip, ACON1, chipi2s->acon1);
+ if (input_type)
+ aw_switch_input_daio(&chipi2s->chip);
+ else
+ aw_switch_input_adc(&chipi2s->chip);
+ if (saa7146_i2s_init_audio_interface(chipi2s, a1, 4, 8) != 0
+ || saa7146_i2s_init_audio_interface(chipi2s, a2, 4, 8) != 0)
+ return -1;
+ err = aw8_device_init(chipi2s);
+ /* enable interrupts */
+ saa7146_write(&chipi2s->chip, IER, (A1_out | A2_out | A1_in));
+ return err;
+}
+
+/**
+ * see aw8.h
+ */
+void aw_exit(struct saa7146i2s *chipi2s)
+{
+ saa7146_write(&chipi2s->chip, IER, 0); /* disable all interrupts */
+ saa7146_write(&chipi2s->chip, MC1, (MRST_N<<16)); /* reset SAA7146 */
+}
+
+/**
+ * see aw8.h
+ */
+void aw_switch_input_adc(struct saa7146reg *chip)
+{
+ switch_input(chip, analog);
+}
+
+/**
+ * see aw8.h
+ */
+void aw_switch_input_daio(struct saa7146reg *chip)
+{
+ switch_input(chip, digital);
+}
+
+/**
+ * see aw8.h
+ * Details regarding implementation see project wiki.
+ */
+int aw8_set_samplerate(struct saa7146reg *chip, unsigned int fs)
+{
+ uint32_t counter = 0;
+ uint8_t fs_prg_seq[4];
+
+ fs = fs > AW_FS_MAX ? AW_FS_MAX : fs;
+ fs = fs < AW_FS_MIN ? AW_FS_MIN : fs;
+ /* init TSA6060 counter */
+ counter = (256 * fs * 4) / PLL_FREF;
+ fs_prg_seq[0] = counter << 1;
+ fs_prg_seq[1] = counter >> 7;
+ fs_prg_seq[2] = (counter >> 15 & 0x03) | 0x18;
+ fs_prg_seq[3] = 0;
+ if (saa7146_i2c_write(chip, bps_10k, PLL_DEVICE_ADDR, NULL, 0,
+ fs_prg_seq, 4) < 4)
+ return -1;
+ return 0;
+}
+
+/**
+ * A1: A2:
+ * analog in: WS0/SD4
+ * analog/digital out: WS1/SD0 analog out: WS3/SD1
+ * analog out: WS2/SD2 analog out: WS4/SD3
+ */
+static int aw8_device_init(struct saa7146i2s *chipi2s)
+{
+ if (saa7146_i2s_init_device(chipi2s, a1, in, ws0, sd4_i_a1) != 0
+ || saa7146_i2s_init_device(chipi2s, a1, out, ws1, sd0_o_a1) != 0
+ || saa7146_i2s_init_device(chipi2s, a1, out, ws2, sd2_io_ax)
+ != 0
+ || saa7146_i2s_init_device(chipi2s, a2, out, ws3, sd1_io_ax)
+ != 0
+ || saa7146_i2s_init_device(chipi2s, a2, out, ws4, sd3_io_ax)
+ != 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * TODO: Description
+ */
+static void switch_input(struct saa7146reg *chip, enum aw_input_types type)
+{
+ /* From EMagic driver...
+ // Switch SAA7146 to safe mode
+ setreg(chip, ACON1, 0L);
+ setreg(chip, ACON2, (12L<<27) | // A1 bitclock == ACLK/384
+ (12L<<22) | // A2 bitclock == ACLK/384
+ ( 1L<<19)); // BCLK1 is input
+ setreg(chip, MC1, 0x7fff0000); // Disable audio i/f
+ setreg(chip, A_TIME_SLOT1, (1L<<TSL_EOS));
+ setreg(chip, A_TIME_SLOT2, (1L<<TSL_EOS));
+ */
+ if (type == analog) {
+ saa7146_write(chip, GPIO_0, MASK_GPIO_HI);
+ /* wait until relais is switched and PLL is locked */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(MAX(1, SLEEP_PLL*HZ/1000));
+ } else {
+ saa7146_write(chip, GPIO_0, MASK_GPIO_LO);
+ /* wait until relais is switched and digital-in is synced. */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(MAX(1, SLEEP_DIGIN*HZ/1000));
+ }
+ /* From EMagic driver...
+ SetReg(MC1, 0x7fff0200, true); // Enable audio i/f
+ */
+}
+
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * Emagic Audiowerk specific functions
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef AUDIOWERK_H_
+#define AUDIOWERK_H_
+
+#include <linux/types.h>
+#include "saa7146.h"
+#include "saa7146i2s.h"
+
+# define AW_MODULE_DESCRIPTION "Emagic Audiowerk 8 Driver"
+# define AW_CARD_SHORT_NAME "Audiowerk 8"
+# define AW_DRIVER_NAME "SAA7146A Audiowerk 8"
+# define AW_PCM_RATE SNDRV_PCM_RATE_CONTINUOUS
+# define AW_FS_MIN 25000 /* fs lower limit for DAC TDA1305 (ADC 18kHz) */
+# define AW_FS_MAX 48000 /* fs upper limit for DAC TDA1305 (ADC 50kHz) */
+# define AW_MAX_PLAYBACK_CHANNELS_PER_STREAM 4
+
+/**
+ * Audiwerk sampling formats.
+ */
+enum aw_formats {fmt_s16_2le, fmt_s16_2be, fmt_s18_4le, fmt_s18_4be,
+ fmt_s20_4le, fmt_s20_4be};
+
+/**
+ * TODO: description
+ */
+int aw_init(struct saa7146i2s *chipi2s, int input_type);
+
+/**
+ * TODO: description
+ */
+void aw_exit(struct saa7146i2s *chipi2s);
+
+/**
+ * Switches the Audiowerk input to analog.
+ * There are two sysclock generators, switched via a relais on GPIO-0:
+ * o TSA 6060 PLL for analog input
+ * o TDA 1315 digital input
+ * @param chip Used for hardware access over PCI bus.
+ */
+void aw_switch_input_adc(struct saa7146reg *chip);
+
+/**
+ * Switches the Audiowerk input to digital.
+ * There are two sysclock generators, switched via a relais on GPIO-0:
+ * o TSA 6060 PLL for analog input
+ * o TDA 1315 digital input
+ * @param chip Used for hardware access over PCI bus.
+ */
+void aw_switch_input_daio(struct saa7146reg *chip);
+
+/**
+ * Set sample rate on AW8.
+ * AW8 supports continuous fs shared by all substreams -> last call to
+ * aw8_set_samplerate() wins.
+ * @param chip Used for hardware access over PCI bus.
+ * @param fs sampling frequency (25-48kHz).
+ * @return 0 in case of success or -1 in case of failure.
+ */
+int aw8_set_samplerate(struct saa7146reg *chip, unsigned int fs);
+
+#endif /*AUDIOWERK_H_*/
2
1
[alsa-devel] [PATCH 1/3] saa7146: Emagic Audiowerk8 low-level ALSA driver
by Matthias Nyffenegger 23 Oct '08
by Matthias Nyffenegger 23 Oct '08
23 Oct '08
From: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
Low-level ALSA driver for Emagic Audiowerk8 sound card.
Project page: http://sourceforge.net/projects/aw8-alsa
Built and tested with Vanilla 2.6.25.16
Signed-off-by: Matthias Nyffenegger <matthias.nyffenegger(a)bluewin.ch>
---
This is a request for submission to ALSA-tree.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/log.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/log.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/log.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/log.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,43 @@
+/*
+ * Kernel log convenience macros
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef LOG_H_
+#define LOG_H_
+#define LOG_ENABLE
+#ifndef SAA7146_SUBSYS_LOG_TAG
+#define SAA7146_SUBSYS_LOG_TAG "SAA7146"
+#endif
+#ifdef LOG_ENABLE
+# define LOG_ERROR(fmt, args...) \
+ printk(KERN_ERR SAA7146_SUBSYS_LOG_TAG ": %s:%d:" fmt "\n", \
+ __func__, __LINE__, ## args);
+# define LOG_WARN(fmt, args...) \
+ printk(KERN_WARNING SAA7146_SUBSYS_LOG_TAG ": %s:%d:" fmt "\n", \
+ __func__, __LINE__, ## args);
+# define LOG_INFO(fmt, args...) \
+ printk(KERN_INFO SAA7146_SUBSYS_LOG_TAG ": %s:%d:" fmt "\n", \
+ __func__, __LINE__, ## args);
+#else
+# define LOG_ERROR(fmt, args...)
+# define LOG_WARN(fmt, args...)
+# define LOG_INFO(fmt, args...)
+#endif /* LOG_ENABLE */
+
+#endif /*LOG_H_*/
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146audio.c
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146audio.c
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146audio.c 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146audio.c 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,343 @@
+/*
+ * SAA7146 audio stream abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/types.h>
+#include "log.h"
+#include "saa7146.h"
+#include "saa7146i2s.h"
+#include "saa7146audio.h"
+
+/**
+ * Address maps for Audio DMA Control PCI_ADP, BaseAx_x, ProtAx_x, PageAx_x and
+ * MC1 Audio Transfer Enable: 1st dim. index corresponds to
+ * enum audio_interfaces's, 2nd to enum directions's.
+ * TODO: decouple from saa7146i2s.h by providing a function that makes a
+ * reliable mapping (independent of int values of enum members).
+ */
+static const int Base[2][2] = {{BaseA1_in, BaseA1_out},
+ {BaseA2_in, BaseA2_out} };
+static const int Prot[2][2] = {{ProtA1_in, ProtA1_out},
+ {ProtA2_in, ProtA2_out} };
+static const int Page[2][2] = {{PageA1_in, PageA1_out},
+ {PageA2_in, PageA2_out} };
+static const int TR_E[2][2] = {{TR_E_A1_IN, TR_E_A1_OUT},
+ {TR_E_A2_IN, TR_E_A2_OUT} };
+static const int PCI_ADP[2][2] = {{PCI_ADP2, PCI_ADP1},
+ {PCI_ADP4, PCI_ADP3} };
+
+/* forward declarations */
+static struct audio_stream *stream_prepare(struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction);
+static int stream_check_resources(struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction);
+static int stream_init(struct saa7146audio *chipaudio,
+ struct audio_stream *stream,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction);
+static int stream_setup_dma(struct saa7146audio *chipaudio,
+ struct audio_stream *audio_stream);
+static int is_valid_period(int period);
+
+/**
+ * see saa7146audio.h
+ */
+struct audio_stream *saa7146_stream_prepare_capture(
+ struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian)
+{
+ return stream_prepare(chipaudio, stream_nr, buffer_base_addr,
+ buffer_size, channel_count, sample_length, endian, in);
+}
+
+/**
+ * see saa7146audio.h
+ */
+struct audio_stream *saa7146_stream_prepare_playback(
+ struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian)
+{
+ return stream_prepare(chipaudio, stream_nr, buffer_base_addr,
+ buffer_size, channel_count, sample_length, endian, out);
+}
+
+/**
+ * see saa7146audio.h
+ */
+void saa7146_stream_unprepare(struct saa7146audio *chipaudio,
+ struct audio_stream *stream)
+{
+ int i = 0;
+ struct i2s_device *device = NULL;
+
+ if (stream != NULL) {
+ stream->number = -1;
+ stream->state = disabled;
+ stream->direction = in;
+ stream->channel_count = 0;
+ stream->sample_length = 0;
+ stream->endian = le;
+ stream->buffer_base_addr = 0;
+ stream->buffer_size = 0;
+ stream->audio_interface = NULL;
+ for (i = 0; i < MAX_I2S_DEVICES; i++) {
+ device = stream->i2s_devices[i];
+ if (device != NULL) {
+ saa7146_i2s_disable_device(&chipaudio->chipi2s,
+ device);
+ stream->i2s_devices[i] = NULL;
+ }
+ }
+ }
+}
+
+/**
+ * see saa7146audio.h
+ */
+void saa7146_stream_start(struct saa7146audio *chipaudio,
+ struct audio_stream *stream)
+{
+ int tr_e;
+
+ if (stream == NULL) {
+ LOG_WARN("stream pointer is NULL");
+ return;
+ }
+ /* enable DMA Ax IN/OUT */
+ tr_e = TR_E[stream->audio_interface->id][stream->direction];
+ saa7146_write(&chipaudio->chipi2s.chip, MC1, (tr_e<<16 | tr_e));
+ saa7146_i2s_enable_audio_interface(&chipaudio->chipi2s,
+ stream->audio_interface);
+}
+
+/**
+ * see saa7146audio.h
+ */
+void saa7146_stream_stop(struct saa7146audio *chipaudio,
+ struct audio_stream *stream)
+{
+ int tr_e;
+
+ if (stream == NULL) {
+ LOG_WARN("stream pointer is NULL");
+ return;
+ }
+ saa7146_i2s_disable_audio_interface(&chipaudio->chipi2s,
+ stream->audio_interface);
+ /* disable DMA Ax IN/OUT */
+ tr_e = TR_E[stream->audio_interface->id][stream->direction];
+ saa7146_write(&chipaudio->chipi2s.chip, MC1, (tr_e<<16));
+}
+
+/**
+ * see saa7146audio.h
+ */
+uint32_t saa7146_stream_get_hw_pointer(struct saa7146audio *chipaudio,
+ struct audio_stream *stream)
+{
+ uint32_t hw_ptr = 0;
+
+ if (stream == NULL) {
+ LOG_WARN("stream pointer is NULL");
+ return hw_ptr;
+ }
+ hw_ptr = saa7146_read(&chipaudio->chipi2s.chip,
+ PCI_ADP[stream->audio_interface->id][stream->direction]);
+ return hw_ptr;
+}
+
+static struct audio_stream *stream_prepare(struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction)
+{
+ struct audio_stream *stream = NULL;
+ int max_streams = (direction == in ?
+ MAX_IN_AUDIO_STREAMS : MAX_OUT_AUDIO_STREAMS);
+ struct audio_stream *streams = (direction == in ?
+ chipaudio->in_streams : chipaudio->out_streams);
+
+ if (stream_nr > max_streams || streams[stream_nr].state == enabled) {
+ LOG_WARN("Stream nr=%d not available", stream_nr);
+ return NULL;
+ }
+ stream = &streams[stream_nr];
+ if (stream_check_resources(chipaudio, stream_nr, channel_count,
+ sample_length, endian, direction) != 0)
+ return NULL;
+ if (stream_init(chipaudio, stream, stream_nr, buffer_base_addr,
+ buffer_size, channel_count, sample_length, endian, direction)
+ != 0)
+ return NULL;
+ if (stream_setup_dma(chipaudio, stream) != 0)
+ return NULL;
+ return stream;
+}
+
+/**
+ * Check if the requested resources (channels, sample-length, endian) are
+ * available.
+ */
+static int stream_check_resources(struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction)
+{
+ int i = 0;
+ int available_channels = 0;
+ int available_frame_length = 0;
+ struct i2s_device *device = NULL;
+ struct audio_interface *ai = NULL;
+
+ ai = &chipaudio->chipi2s.audio_interfaces[stream_nr];
+ available_frame_length = ai->i2s_superframe_length;
+ for (i = 0; i < MAX_I2S_DEVICES; i++) {
+ device = ai->i2s_devices[i];
+ if (device != NULL && device->direction == direction) {
+ if (device->state == disabled)
+ available_channels += 2;
+ else if (device->state == enabled &&
+ device->direction == in &&
+ device->endian != endian) {
+ LOG_WARN("Endian %s not available for capture"
+ " on audio-interface %s.",
+ (endian == le ? "le" : "be"),
+ ai->id == a1 ? "A1" : "A2");
+ return -1;
+ } else {
+ available_frame_length -=
+ 2 * device->sample_length;
+ }
+ }
+ }
+ if (available_channels < channel_count ||
+ available_frame_length < (channel_count * sample_length)) {
+ LOG_WARN("Not sufficient resources available.");
+ return -1;
+ }
+ return 0;
+}
+
+static int stream_init(struct saa7146audio *chipaudio,
+ struct audio_stream *stream,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian,
+ enum directions direction)
+{
+ int i;
+ struct i2s_device *device = NULL;
+
+ stream->number = stream_nr;
+ stream->state = enabled;
+ stream->direction = direction;
+ stream->channel_count = channel_count;
+ stream->sample_length = sample_length;
+ stream->endian = endian;
+ stream->buffer_base_addr = buffer_base_addr;
+ stream->buffer_size = buffer_size;
+ stream->audio_interface =
+ &chipaudio->chipi2s.audio_interfaces[stream_nr];
+ for (i = 0; (i < MAX_I2S_DEVICES) && (channel_count > 0); i++) {
+ device = stream->audio_interface->i2s_devices[i];
+ if (device != NULL && device->state == disabled &&
+ device->direction == direction) {
+ if (saa7146_i2s_enable_device(&chipaudio->chipi2s,
+ device,
+ sample_length,
+ endian) != 0)
+ return -1;
+ /* use ws as index where device is stored in array */
+ stream->i2s_devices[device->ws] = device;
+ channel_count -= 2;
+ }
+ }
+ return 0;
+}
+
+static int stream_setup_dma(struct saa7146audio *chipaudio,
+ struct audio_stream *stream)
+{
+ int exp = 0;
+ int limit = 0;
+ enum audio_interfaces ai_nr = stream->audio_interface->id;
+ enum directions dir = stream->direction;
+ struct saa7146reg *chip = &chipaudio->chipi2s.chip;
+
+ if (!is_valid_period(stream->buffer_size >> 1)) {
+ LOG_ERROR("Invalid buffer size %d", stream->buffer_size);
+ return -1;
+ }
+ /* find exponent for period, required to figure out the DMA IRQ limit */
+ limit = stream->buffer_size >> 1; /* period is buffer_size/2 */
+ for (exp = 0; limit > 0; limit >>= 1, exp++);
+ /* LimitAx_in = exp - 6 (minimum limit is 64bytes = 2^6) */
+ limit = exp - 6;
+ saa7146_write(chip, Base[ai_nr][dir], stream->buffer_base_addr);
+ saa7146_write(chip, Prot[ai_nr][dir],
+ stream->buffer_base_addr + stream->buffer_size);
+ /* PageA1_in=0, MEA1_in=0, LimitA1_in=limit, PVA1_in=0 */
+ saa7146_write(chip, Page[a1][dir], limit << 4);
+ return 0;
+}
+
+/**
+ * Period size must be >= 64bytes <= 1Mb and a power of 2.
+ * This is a restriction imposed by the SAA7146 DMA capabilities: the period at
+ * which DMA IRQs are generated is 64*2^n where n={1-14}.
+ * @return 1 if the given period is valid.
+ */
+static int is_valid_period(int period)
+{
+ return (period >= 64) && (period <= (1 << 20)) && is_pow_of_2(period);
+}
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146audio.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146audio.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146audio.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146audio.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * SAA7146 audio stream abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef SAA7146AUDIO_H_
+#define SAA7146AUDIO_H_
+
+#include <linux/types.h>
+#include "saa7146i2s.h"
+
+#define MAX_IN_AUDIO_STREAMS 2 /* SAA7146 has 2 IN-DMA channels */
+#define MAX_OUT_AUDIO_STREAMS 2 /* SAA7146 has 2 OUT-DMA channels */
+
+/**
+ * TODO: description
+ * Some of this struct's members also occur on i2s_device: this is because
+ * the 'stream abstraction' is built on top of the 'i2s abstraction'.
+ */
+struct audio_stream {
+ int number; /* stream number - corresponds to ALSA subdevice number */
+ enum states state;
+ enum directions direction;
+ int channel_count;
+ int sample_length; /* all stream channels have equal sample length */
+ enum endian endian; /* all stream channels have equal endian */
+ unsigned long buffer_base_addr;
+ int buffer_size;
+ /* a stream is controlled by one audio-interface */
+ struct audio_interface *audio_interface;
+ /* a stream can have multiple i2c devices (one per stereo channels) */
+ struct i2s_device *i2s_devices[MAX_I2S_DEVICES];
+};
+
+/**
+ * TODO: description
+ */
+struct saa7146audio {
+ struct saa7146i2s chipi2s;
+ /* we are the 'owner' of all streams */
+ struct audio_stream in_streams[MAX_IN_AUDIO_STREAMS];
+ struct audio_stream out_streams[MAX_OUT_AUDIO_STREAMS];
+};
+
+/**
+ * TODO: description
+ * @param chipaudio used for SAA7146 I2S ctrl and register access over PCI bus,
+ * must not be NULL.
+ * @return A reference to an audio_stream structure, or NULL in case the
+ * stream could not be opened.
+ */
+struct audio_stream *saa7146_stream_prepare_capture(
+ struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian);
+
+/**
+ * TODO: description
+ * @param chipaudio used for SAA7146 I2S ctrl and register access over PCI bus,
+ * must not be NULL.
+ * @return A reference to an audio_stream structure, or NULL in case the
+ * stream could not be opened.
+ */
+struct audio_stream *saa7146_stream_prepare_playback(
+ struct saa7146audio *chipaudio,
+ unsigned int stream_nr,
+ unsigned long buffer_base_addr,
+ int buffer_size,
+ int channel_count,
+ int sample_length,
+ enum endian endian);
+
+/**
+ * TODO: description
+ * @param chipaudio used for SAA7146 I2S ctrl and register access over PCI bus,
+ * must not be NULL.
+ * @param stream A reference to an audio_stream structure, opened with
+ * saa7146_open_xxxxx_stream().
+ */
+void saa7146_stream_unprepare(struct saa7146audio *chipaudio,
+ struct audio_stream *stream);
+
+/**
+ * TODO: description
+ */
+void saa7146_stream_start(struct saa7146audio *chipaudio,
+ struct audio_stream *stream);
+
+/**
+ * TODO: description
+ */
+void saa7146_stream_stop(struct saa7146audio *chipaudio,
+ struct audio_stream *stream);
+
+/**
+ * TODO: description
+ */
+uint32_t saa7146_stream_get_hw_pointer(struct saa7146audio *chipaudio,
+ struct audio_stream *stream);
+
+#endif /*SAA7146AUDIO_H_*/
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,216 @@
+/*
+ * SAA7146 register access abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ * Based on GPLed Emagic Audiowerk8 Windows driver provided by Martijn Sipkema.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef SAA7146_H_
+#define SAA7146_H_
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/**
+ * SAA7146 chip access data structure
+ */
+struct saa7146reg {
+ unsigned long iobase_phys;
+ void __iomem *iobase_virt;
+ int iolen;
+};
+
+/**
+ * Read an SAA7146 register, mind that PCI is little-endian.
+ * @param chip used for SAA7146 register access over PCI bus
+ * @param offset register address offset
+ * @return contents of register at the given address offset
+ */
+static inline uint32_t saa7146_read(struct saa7146reg *chip, int offset)
+{
+ return __le32_to_cpu(readl(chip->iobase_virt + offset));
+}
+
+/**
+ * Write an SAA7146 register, mind that PCI is little-endian.
+ * @param chip used for SAA7146 register access over PCI bus
+ * @param offset register address offset
+ * @param data the data to be written at the given address offset
+ */
+static inline void saa7146_write(struct saa7146reg *chip,
+ int offset,
+ uint32_t data)
+{
+ writel(__cpu_to_le32(data), chip->iobase_virt + offset);
+}
+
+/* SAA7146 register offset */
+#define PCI_BT_A 0x4C
+#define IICTRF 0x8C
+#define IICSTA 0x90
+#define BaseA1_in 0x94
+#define ProtA1_in 0x98
+#define PageA1_in 0x9C
+#define BaseA1_out 0xA0
+#define ProtA1_out 0xA4
+#define PageA1_out 0xA8
+#define BaseA2_in 0xAC
+#define ProtA2_in 0xB0
+#define PageA2_in 0xB4
+#define BaseA2_out 0xB8
+#define ProtA2_out 0xBC
+#define PageA2_out 0xC0
+#define IER 0xDC
+#define GPIO_CTRL 0xE0
+#define ACON1 0xF4
+#define ACON2 0xF8
+#define MC1 0xFC
+#define MC2 0x100
+#define ISR 0x10C
+#define PSR 0x110
+#define SSR 0x114
+#define PCI_ADP1 0x12C
+#define PCI_ADP2 0x130
+#define PCI_ADP3 0x134
+#define PCI_ADP4 0x138
+#define LEVEL_REP 0x140
+#define FB_BUFFER1 0x144
+#define FB_BUFFER2 0x148
+#define TSL1 0x180
+#define TSL2 0x1C0
+
+/* PSR/ISR/IER */
+#define PPEF (1UL << 31)
+#define PABO (1UL << 30)
+#define IIC_S (1UL << 17)
+#define IIC_E (1UL << 16)
+#define A2_in (1UL << 15)
+#define A2_out (1UL << 14)
+#define A1_in (1UL << 13)
+#define A1_out (1UL << 12)
+#define AFOU (1UL << 11)
+#define PIN3 (1UL << 6)
+#define PIN2 (1UL << 5)
+#define PIN1 (1UL << 4)
+#define PIN0 (1UL << 3)
+#define ECS (1UL << 2)
+#define EC3S (1UL << 1)
+#define EC0S (1UL << 0)
+
+/* SSR */
+#define PRQ (1UL << 31)
+#define PMA (1UL << 30)
+#define IIC_EA (1UL << 21)
+#define IIC_EW (1UL << 20)
+#define IIC_ER (1UL << 19)
+#define IIC_EL (1UL << 18)
+#define IIC_EF (1UL << 17)
+#define AF2_in (1UL << 10)
+#define AF2_out (1UL << 9)
+#define AF1_in (1UL << 8)
+#define AF1_out (1UL << 7)
+#define EC5S (1UL << 3)
+#define EC4S (1UL << 2)
+#define EC2S (1UL << 1)
+#define EC1S (1UL << 0)
+
+/* PCI_BT_A */
+#define BurstA1_in (1UL << 26)
+#define ThreshA1_in (1UL << 24)
+#define BurstA1_out (1UL << 18)
+#define ThreshA1_out (1UL << 16)
+#define BurstA2_in (1UL << 10)
+#define ThreshA2_in (1UL << 8)
+#define BurstA2_out (1UL << 2)
+#define ThreshA2_out (1UL << 0)
+
+/* MC1 */
+#define MRST_N (1UL << 15)
+#define EAP (1UL << 9)
+#define EI2C (1UL << 8)
+#define TR_E_A2_OUT (1UL << 3)
+#define TR_E_A2_IN (1UL << 2)
+#define TR_E_A1_OUT (1UL << 1)
+#define TR_E_A1_IN (1UL << 0)
+
+/* MC2 */
+#define UPLD_IIC (1UL << 0)
+
+/* ACON1 bit offsets */
+#define ACON1_AUDIO_MODE 29
+#define ACON1_MAXLEVEL 22
+#define ACON1_A1_SWAP 21
+#define ACON1_A2_SWAP 20
+#define ACON1_WS0_CTRL 18
+#define ACON1_WS0_SYNC 16
+#define ACON1_WS1_CTRL 14
+#define ACON1_WS1_SYNC 12
+#define ACON1_WS2_CTRL 10
+#define ACON1_WS2_SYNC 8
+#define ACON1_WS3_CTRL 6
+#define ACON1_WS3_SYNC 4
+#define ACON1_WS4_CTRL 2
+#define ACON1_WS4_SYNC 0
+
+/* ACON2 bit offsets */
+#define ACON2_A1_CLKSRC 27
+#define ACON2_A2_CLKSRC 22
+#define ACON2_INVERT_BCLK1 21
+#define ACON2_INVERT_BCLK2 20
+#define ACON2_BCLK1_OEN 19
+#define ACON2_BCLK2_OEN 18
+
+/* TSL bit offsets */
+#define TSL_EOS 0
+#define TSL_LOW_A2 1
+#define TSL_DOD_A2 2
+#define TSL_BSEL_A2 4
+#define TSL_LF_A2 7
+#define TSL_SF_A2 8
+#define TSL_SIB_A2 9
+#define TSL_SDW_A2 10
+#define TSL_DIS_A2 11
+#define TSL_LOW_A1 14
+#define TSL_DOD_A1 15
+#define TSL_BSEL_A1 17
+#define TSL_LF_A1 20
+#define TSL_SF_A1 21
+#define TSL_SIB_A1 22
+#define TSL_SDW_A1 23
+#define TSL_DIS_A1 24
+#define TSL_WS4 27
+#define TSL_WS3 28
+#define TSL_WS2 29
+#define TSL_WS1 30
+#define TSL_WS0 31
+
+/* SD pin select for DIS_Ax and DOD_Ax */
+#define SD0_I_A2 0
+#define SD0_O_A1 0
+#define SD1_IO_Ax 1
+#define SD2_IO_Ax 2
+#define SD3_IO_Ax 3
+#define SD4_I_A1 0
+#define SD4_O_A2 0
+
+/* ACON1 WSx_CTRL select */
+#define ACON1_WSx_CTRL_IN_TSLx 0
+#define ACON1_WSx_CTRL_OUT_TSL1 1
+#define ACON1_WSx_CTRL_OUT_TSL2 2
+#define ACON1_WSx_CTRL_LOW 3
+#define ACON1_WSx_SYNC_I2S 0
+
+#endif /*SAA7146_H_*/
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2c.c
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2c.c
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2c.c 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2c.c 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,270 @@
+/*
+ * SAA7146 I2C-bus abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include "log.h"
+#include "saa7146.h"
+#include "saa7146i2c.h"
+
+/* Convenience macros */
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/**
+ * See SAA7146 tech. specification p.122 - 126 for details.
+ */
+
+#define IICSTA_ABORT 0x80
+#define IICSTA_ERR_BSY 0x7f
+#define MC1_EI2C 0x01000000
+#define MC2_UPLD_IIC 0x00010000
+#define START 3
+#define CONT 2
+#define STOP 1
+#define NOP 0
+#define RW 1
+
+/**
+ * SAA7146 available i2c clock rates to Hz mapping:
+ * The i2c status register clock-rate value is equal to the array index of its
+ * Hz value => order matters!
+ */
+static const unsigned int i2c_clk_2_hz[] = {
+ 266000, /* PCI clock/120 at index 0 */
+ 10000, /* PCI clock/3200 at index 1 */
+ 400000, /* PCI clock/80 at index 2 */
+ 533000, /* PCI clock/60 at index 3 */
+ 66000, /* PCI clock/480 at index 4 */
+ 5000, /* PCI clock/6400 at index 5 */
+ 100000, /* PCI clock/320 at index 6 */
+ 133000, /* PCI clock/240 at index 7 */
+};
+
+/* forward declarations */
+static int i2c_open(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ unsigned int sub_addr_size);
+static int i2c_transmit(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t *buf,
+ unsigned int buf_size);
+static int i2c_transmit_byte(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t *byte,
+ int cmd);
+static void i2c_prepare(struct saa7146reg *chip, enum saa7146_i2c_clock clk);
+static void i2c_upload(struct saa7146reg *chip, int offset, uint32_t data);
+static void i2c_wait(enum saa7146_i2c_clock clk, int bytes_to_send);
+static void i2c_abort(struct saa7146reg *chip);
+
+/**
+ * see saa7146i2c.h
+ */
+int saa7146_i2c_read(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ int sub_addr_size,
+ uint8_t *buffer,
+ int size)
+{
+ uint32_t ret_val = 0;
+
+ ret_val = i2c_open(chip, clk, RW, dev_addr, sub_addr, sub_addr_size);
+ if (ret_val >= 0)
+ ret_val = i2c_transmit(chip, clk, RW, buffer, size);
+ return ret_val;
+}
+
+/**
+ * see saa7146i2c.h
+ */
+int saa7146_i2c_write(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ int sub_addr_size,
+ uint8_t *data,
+ int size)
+{
+ uint32_t ret_val = 0;
+
+ ret_val = i2c_open(chip, clk, !RW, dev_addr, sub_addr, sub_addr_size);
+ if (ret_val >= 0)
+ ret_val = i2c_transmit(chip, clk, !RW, data, size);
+ return ret_val;
+}
+
+/**
+ * Open an i2c device:
+ * 1) transmit device-address, r/w == low and startcondition
+ * 2) optionally transmit subaddress
+ * 3) in case of READ operation, transmit device-address again with r/w == high
+ * and startcondition.
+ * @return number of address bytes sent or -1 in case of failure.
+ */
+static int i2c_open(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ unsigned int sub_addr_size)
+{
+ int sub_addr_bytes_transmitted = 0;
+ uint8_t addr = 0;
+
+ i2c_prepare(chip, clk);
+ addr = (dev_addr<<1)&~RW;
+ if (i2c_transmit_byte(chip, clk, read, &addr, START) != 0) {
+ LOG_ERROR("i2c address phase");
+ return -1;
+ }
+ while (sub_addr_bytes_transmitted < sub_addr_size) {
+ if (i2c_transmit_byte(chip, clk, read, sub_addr++, CONT) != 0) {
+ LOG_ERROR("i2c subaddress phase");
+ return -1;
+ }
+ sub_addr_bytes_transmitted++;
+ }
+ if (read) {
+ addr = (dev_addr<<1)|RW;
+ if (i2c_transmit_byte(chip, clk, read, &addr, START) != 0) {
+ LOG_ERROR("i2c prepare READ phase");
+ return -1;
+ }
+ }
+ return sub_addr_bytes_transmitted;
+}
+
+/**
+ * Transmit data on i2c-bus: transfer is closed when all data is transmitted.
+ * Call this function after i2c_open().
+ * @return number of bytes transmitted or -1 in case of failure.
+ */
+static int i2c_transmit(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t *buffer,
+ unsigned int size)
+{
+ int bytes_transmitted = 0;
+ int attr = 0;
+
+ while (bytes_transmitted < size) {
+ attr = (bytes_transmitted++ < (size - 1) ? CONT : STOP);
+ if (i2c_transmit_byte(chip, clk, read, buffer++, attr) != 0) {
+ LOG_ERROR("i2c data transmission phase");
+ return -1;
+ }
+ }
+ saa7146_write(chip, MC1, MC1_EI2C | 0); /* disable i2c */
+ return bytes_transmitted;
+}
+
+/**
+ * In order to keep the implementation simple, we transmit only one byte per
+ * i2c-upload (IICTRF BYTE2).
+ */
+static int i2c_transmit_byte(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clk,
+ int read,
+ uint8_t *byte,
+ int attr)
+{
+ uint32_t iictrf = 0; /* BYTE0..2 = 0, ATTR0..2 = NOP */
+ uint32_t iicsta = 0;
+
+ iictrf = (*byte) << (8*3) | attr << (2*3);
+ i2c_upload(chip, IICTRF, iictrf);
+ i2c_wait(clk, 1);
+ iicsta = saa7146_read(chip, IICSTA);
+ if ((iicsta & IICSTA_ERR_BSY) != 0) {
+ i2c_abort(chip);
+ LOG_ERROR("IICSTA=%#010lx", (long)iicsta);
+ return -1;
+ }
+ if (read) {
+ iictrf = saa7146_read(chip, IICTRF);
+ *byte = iictrf >> (8*3);
+ }
+ return 0;
+}
+
+/**
+ * Enable i2c, abort all pending operations, clear all errors (may be needed 2x
+ * after abort) and set clock rate.
+ */
+static void i2c_prepare(struct saa7146reg *chip, enum saa7146_i2c_clock clk)
+{
+ saa7146_write(chip, MC1, MC1_EI2C | EI2C);
+ i2c_upload(chip, IICSTA, IICSTA_ABORT);
+ i2c_upload(chip, IICSTA, 0);
+ i2c_upload(chip, IICSTA, (clk << 8));
+}
+
+/**
+ * Upload I2C register IICSTA and IICTRF from shadow RAM.
+ */
+static void i2c_upload(struct saa7146reg *chip, int offset, uint32_t data)
+{
+ if ((offset == IICSTA) || (offset == IICTRF)) {
+ saa7146_write(chip, offset, data);
+ saa7146_write(chip, MC2, (MC2_UPLD_IIC | UPLD_IIC));
+ }
+}
+
+/**
+ * Wait for a i2c transmission to complete.
+ * The required delay is calculated on the basis of the given clock-rate and
+ * the number of bytes to transmit:
+ * - each byte is followed by ACK i.e. there are 9 clock-cycles/byte, we add
+ * one for safety
+ * - the kernel's scheduler tick (HZ) is used as time base and we add one tick
+ * for safety
+ *
+ * delay[ms] = (bytes_to_send*10[bits]/clock[1/s]*1000)*HZ/1000 + 1
+ *
+ * The function returns after the required delay, regardless of the i2c state.
+ */
+static void i2c_wait(enum saa7146_i2c_clock clk, int bytes_to_send)
+{
+ int delay = 0;
+
+ delay = ((bytes_to_send*10*HZ)/i2c_clk_2_hz[clk]) + 1;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(MAX(1, delay));
+}
+
+/**
+ * Abort all pending operations, clear all errors (may be needed 2x after abort)
+ * set clock rate to lowset possible and finally disable i2c.
+ */
+static void i2c_abort(struct saa7146reg *chip)
+{
+ i2c_upload(chip, IICSTA, IICSTA_ABORT);
+ i2c_upload(chip, IICSTA, 0);
+ i2c_upload(chip, IICSTA, (bps_5k << 8));
+ saa7146_write(chip, MC1, MC1_EI2C | 0);
+}
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2c.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2c.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2c.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2c.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,81 @@
+/*
+ * SAA7146 I2C-bus abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef SAA7146I2C_H_
+#define SAA7146I2C_H_
+
+#include <linux/types.h>
+#include "saa7146.h"
+
+/**
+ * SAA7146 available i2c clock rates (reg IICSTA IICCC[2:0] bit 8..10)
+ */
+enum saa7146_i2c_clock {
+ bps_5k = 5, /* PCI clock/6400 */
+ bps_10k = 1, /* PCI clock/3200 */
+ bps_66k = 4, /* PCI clock/480 */
+ bps_100k = 6, /* PCI clock/320 */
+ bps_133k = 7, /* PCI clock/240 */
+ bps_266k = 0, /* PCI clock/120 */
+ bps_400k = 2, /* PCI clock/80 */
+ bps_533k = 3 /* PCI clock/60 */
+};
+
+/**
+ * Write n bytes to i2c bus.
+ * @param chip Used for SAA7146 register access over PCI bus
+ * @param clock One of the SAA7146 available i2c bit rates
+ * @param dev_addr the i2c (slave-) device address 7-bit
+ * @param sub_addr the i2c (slave-) device sub-address
+ * @param sub_addr_size The size of the sub-address, can be 0 if no sub-address
+ * is provided
+ * @param data Data to be written
+ * @param size Size of data to be written, must be >= 0
+ * @return Number of bytes written or -1 in case of failure
+ */
+int saa7146_i2c_write(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clock,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ int sub_addr_size,
+ uint8_t *data,
+ int size);
+
+/**
+ * Read n bytes from i2c bus.
+ * @param chip Used for SAA7146 register access over PCI bus
+ * @param clock One of the SAA7146 available i2c bit rates
+ * @param dev_addr the i2c (slave-) device address 7-bit
+ * @param sub_addr the i2c (slave-) device sub-address
+ * @param sub_addr_size The size of the sub-address, can be 0 if no sub-address
+ * is provided
+ * @param buffer Data buffer for bytes to be read.
+ * @param size Size of data to be read, must be >= 0.
+ * @return Number of bytes read or -1 in case of failure.
+ */
+int saa7146_i2c_read(struct saa7146reg *chip,
+ enum saa7146_i2c_clock clock,
+ uint8_t dev_addr,
+ uint8_t *sub_addr,
+ int sub_addr_size,
+ uint8_t *buffer,
+ int size);
+
+#endif /*SAA7146I2C_H_*/
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2s.c
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2s.c
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2s.c 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2s.c 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,437 @@
+/*
+ * SAA7146 I2S and audio-interface abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/types.h>
+#include "log.h"
+#include "saa7146.h"
+#include "saa7146i2s.h"
+
+/* Convenience macros */
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define DWBUF_SIZE 4 /* SAA7146 audiointerface's DWORD buffer size */
+#define MAX_SUPERFRAME_LENGTH 16 /* 32 byte superframe is not supported */
+#define MIN_SUPERFRAME_LENGTH DWBUF_SIZE /* must be >= DWORD buffer length */
+
+/**
+ * TSL address map: array pos. corresponds to enum audio_interfaces
+ */
+static const int TSLx[2] = {TSL1, TSL2};
+
+/**
+ * TSL bit offse maps: array pos. corresponds to enum audio_interfaces
+ */
+static const int TSL_DIS_Ax[2] = {TSL_DIS_A1, TSL_DIS_A2};
+static const int TSL_SIB_Ax[2] = {TSL_SIB_A1, TSL_SIB_A2};
+static const int TSL_SDW_Ax[2] = {TSL_SDW_A1, TSL_SDW_A2};
+static const int TSL_SF_Ax[2] = {TSL_SF_A1, TSL_SF_A2};
+static const int TSL_LF_Ax[2] = {TSL_LF_A1, TSL_LF_A2};
+static const int TSL_BSEL_Ax[2] = {TSL_BSEL_A1, TSL_BSEL_A2};
+static const int TSL_DOD_Ax[2] = {TSL_DOD_A1, TSL_DOD_A2};
+
+/**
+ * ACON1 SWAP_Ax bit offset map: array pos. corresponds to enum audio_interfaces
+ */
+static const int ACON1_Ax_SWAP[2] = {ACON1_A1_SWAP, ACON1_A2_SWAP};
+
+/**
+ * ACON1 WSx_CTRL value map: array pos. correspond to enum audio_interfaces
+ */
+static const int ACON1_WSx_CTRL_OUT_TSLx[2] = {ACON1_WSx_CTRL_OUT_TSL1,
+ ACON1_WSx_CTRL_OUT_TSL2};
+
+/**
+ * ACON1 WSx_CTRL bit offset map: array pos. corresponds to enum ws_lines
+ */
+static const int ACON1_WSx_CTRL[5] = {ACON1_WS0_CTRL,
+ ACON1_WS1_CTRL,
+ ACON1_WS2_CTRL,
+ ACON1_WS3_CTRL,
+ ACON1_WS4_CTRL};
+
+/**
+ * ACON1 WSx_SYNC bit offset map: array pos. corresponds to enum ws_lines
+ */
+static const int ACON1_WSx_SYNC[5] = {ACON1_WS0_SYNC,
+ ACON1_WS1_SYNC,
+ ACON1_WS2_SYNC,
+ ACON1_WS3_SYNC,
+ ACON1_WS4_SYNC};
+
+/**
+ * TSL WSx bit offset map: array pos. correspond to enum ws_lines
+ */
+static const int TSL_WSx[5] = {TSL_WS0,
+ TSL_WS1,
+ TSL_WS2,
+ TSL_WS3,
+ TSL_WS4};
+
+/**
+ * TSL SDx bit value map: array pos. corresponds to enum sd_lines
+ */
+static const int TSL_SDx[7] = {SD0_I_A2,
+ SD0_O_A1,
+ SD1_IO_Ax,
+ SD2_IO_Ax,
+ SD3_IO_Ax,
+ SD4_I_A1,
+ SD4_O_A2};
+
+/* forward declarations */
+static int tsl_update(struct saa7146i2s *chipi2s,
+ struct audio_interface *audio_interface);
+static int tsl_build(struct audio_interface *audio_interface, uint32_t tsl[]);
+static int tsl_align_devices(struct audio_interface *audio_interface);
+static void tsl_prepare_capture_slot(struct i2s_device *dev,
+ uint32_t tsl[],
+ int slot,
+ int *total_bytes_cap);
+static void tsl_prepare_playback_slot(struct i2s_device *dev,
+ uint32_t tsl[],
+ int slot,
+ int *total_bytes_pbk,
+ int *bsel);
+static void setbits(uint32_t *reg,
+ unsigned int offset,
+ unsigned int len,
+ uint32_t val);
+
+/**
+ * see saa7146i2s.h
+ */
+int saa7146_i2s_init_audio_interface(struct saa7146i2s *chipi2s,
+ enum audio_interfaces ai_id,
+ int i2s_word_length,
+ int i2s_superframe_length)
+{
+ struct audio_interface *ai = NULL;
+
+ if (i2s_word_length > DWBUF_SIZE || i2s_word_length <= 0
+ || !is_pow_of_2(i2s_word_length)) {
+ LOG_ERROR("Invalid i2s word-length %d", i2s_word_length);
+ return -1;
+ }
+ if (i2s_superframe_length > MAX_SUPERFRAME_LENGTH ||
+ i2s_superframe_length < MIN_SUPERFRAME_LENGTH ||
+ !is_pow_of_2(i2s_superframe_length / i2s_word_length)) {
+ LOG_ERROR("Invalid i2s superframe-length %d",
+ i2s_superframe_length);
+ return -1;
+ }
+ ai = &chipi2s->audio_interfaces[ai_id];
+ ai->id = ai_id;
+ ai->i2s_word_length = i2s_word_length;
+ ai->i2s_superframe_length = i2s_superframe_length;
+ return 0;
+}
+
+/**
+ * see saa7146i2s.h
+ */
+void saa7146_i2s_enable_audio_interface(struct saa7146i2s *chipi2s,
+ struct audio_interface *ai)
+{
+ /* set AUDIO_MODE A1 and A2 independent */
+ if (ai->id == a1)
+ setbits(&chipi2s->acon1, ACON1_AUDIO_MODE, 1, 1);
+ else if (ai->id == a2)
+ setbits(&chipi2s->acon1, ACON1_AUDIO_MODE + 1, 1, 1);
+ saa7146_write(&chipi2s->chip, ACON1, chipi2s->acon1);
+}
+
+/**
+ * see saa7146i2s.h
+ */
+void saa7146_i2s_disable_audio_interface(struct saa7146i2s *chipi2s,
+ struct audio_interface *ai)
+{
+ /* set AUDIO_MODE A1 and A2 independent */
+ if (ai->id == a1)
+ setbits(&chipi2s->acon1, ACON1_AUDIO_MODE, 1, 0);
+ else if (ai->id == a2)
+ setbits(&chipi2s->acon1, ACON1_AUDIO_MODE + 1, 1, 0);
+ saa7146_write(&chipi2s->chip, ACON1, chipi2s->acon1);
+}
+
+/**
+ * see saa7146i2s.h
+ */
+int saa7146_i2s_init_device(struct saa7146i2s *chipi2s,
+ enum audio_interfaces ai_id,
+ enum directions dir,
+ enum ws_lines ws,
+ enum sd_lines sd)
+{
+ struct i2s_device *dev = NULL;
+
+ if (ws < MAX_I2S_DEVICES) {
+ dev = &chipi2s->i2s_devices[ws];
+ dev->ws = ws;
+ dev->sd = sd;
+ dev->sample_length = 0;
+ dev->endian = le;
+ dev->state = disabled;
+ dev->direction = dir;
+ dev->hw_mon = notmonitored;
+ dev->audio_interface = &chipi2s->audio_interfaces[ai_id];
+ dev->audio_interface->i2s_devices[ws] = dev;
+ /* set WS-Line active low (disable) by default */
+ setbits(&chipi2s->acon1, ACON1_WSx_CTRL[ws], 2,
+ ACON1_WSx_CTRL_LOW);
+ } else {
+ LOG_ERROR("invalid ws=%d", ws);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * see saa7146i2s.h
+ */
+int saa7146_i2s_enable_device(struct saa7146i2s *chipi2s,
+ struct i2s_device *dev,
+ int sample_len,
+ enum endian endian)
+{
+ dev->sample_length = sample_len;
+ dev->endian = endian;
+ dev->state = enabled;
+ if (dev->direction == in) {
+ /* i2s WS config - for capture devices we assume slave mode */
+ setbits(&chipi2s->acon1, ACON1_WSx_CTRL[dev->ws], 2,
+ ACON1_WSx_CTRL_IN_TSLx);
+ /* handle endianness (see documentation in wiki) */
+ setbits(&chipi2s->acon1,
+ ACON1_Ax_SWAP[dev->audio_interface->id], 1,
+ (dev->endian == le) ? 0 : 1);
+ }
+ if (dev->direction == out) {
+ /* i2s WS config - for playback devices we assume master mode */
+ setbits(&chipi2s->acon1, ACON1_WSx_CTRL[dev->ws], 2,
+ ACON1_WSx_CTRL_OUT_TSLx[dev->audio_interface->id]);
+ }
+ setbits(&chipi2s->acon1, ACON1_WSx_SYNC[dev->ws], 2,
+ ACON1_WSx_SYNC_I2S);
+ saa7146_write(&chipi2s->chip, ACON1, chipi2s->acon1);
+ if (tsl_update(chipi2s, dev->audio_interface) != 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * see saa7146i2s.h
+ */
+int saa7146_i2s_disable_device(struct saa7146i2s *chipi2s,
+ struct i2s_device *dev)
+{
+ dev->sample_length = 0;
+ dev->endian = le;
+ dev->state = disabled;
+ dev->hw_mon = notmonitored;
+ /* set WS-Line active low (disable) by default */
+ setbits(&chipi2s->acon1, ACON1_WSx_CTRL[dev->ws], 2,
+ ACON1_WSx_CTRL_LOW);
+ if (tsl_update(chipi2s, dev->audio_interface) != 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * TODO: description
+ * TSL1 is always associated with A1, TSL2 with A2.
+ */
+static int tsl_update(struct saa7146i2s *chipi2s,
+ struct audio_interface *ai)
+{
+ int slot = 0;
+ uint32_t tsl[ai->i2s_superframe_length];
+
+ if (tsl_align_devices(ai) != 0)
+ return -1;
+ /* reset TSL array! */
+ for (slot = 0; slot < ai->i2s_superframe_length; tsl[slot++] = 0);
+ if (tsl_build(ai, tsl) == 0) {
+ for (slot = 0; slot < ai->i2s_superframe_length; slot++) {
+ saa7146_write(&chipi2s->chip,
+ TSLx[ai->id] + (4 * slot), tsl[slot]);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/**
+ * Build the TSL for the given audio-interface.
+ * The TSL is built from scratch, based on the properties of the devices that
+ * are controlled by the associated audio-interface.
+ * This function does not write the TSL down to the HW. This is done in
+ * tsl_update() - i.e. if this function fails the current TSL is not changed.
+ */
+static int tsl_build(struct audio_interface *ai, uint32_t tsl[])
+{
+ int i = 0;
+ int slot = 0;
+ int bsel = 0;
+ int total_bytes_cap = 0;
+ int total_bytes_pbk = 0;
+ struct i2s_device *dev = NULL;
+ int max_slots = ai->i2s_superframe_length;
+
+ for (slot = 0; slot < max_slots; slot++) {
+ for (i = 0; i < MAX_I2S_DEVICES; i++) {
+ dev = ai->i2s_devices[i];
+ if (dev != NULL && dev->state == enabled) {
+ if (dev->direction == in) {
+ tsl_prepare_capture_slot(dev, tsl,
+ slot, &total_bytes_cap);
+ } else if (dev->direction == out) {
+ tsl_prepare_playback_slot(dev, tsl,
+ slot, &total_bytes_pbk, &bsel);
+ }
+ }
+ }
+ /* Reset TSL pointer in the last timeslot */
+ setbits(&tsl[slot], TSL_EOS, 1, (slot == max_slots-1 ? 1 : 0));
+ }
+ if (total_bytes_cap > 0 && total_bytes_cap < DWBUF_SIZE) {
+ LOG_ERROR("Total sample-length of capture devices"
+ " on audio-interface %d < DWORD-buffer size => buffer"
+ " is never transfered to DMA", ai->id);
+ return -1;
+ }
+ if (total_bytes_pbk > 0 && total_bytes_pbk < DWBUF_SIZE) {
+ LOG_ERROR("Total sample-length of playback devices"
+ " on audio-interface %d < DWORD-buffer size => buffer"
+ " is never reloaded from DMA", ai->id);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Assign a TSL slot to each i2s-device on the given audio-interface: this is
+ * the TSL slot where a given i2s-device's first sample byte of its first
+ * channel is captured/played back.
+ */
+static int tsl_align_devices(struct audio_interface *ai)
+{
+ int i = 0;
+ int next_pbk_dev_start_slot = 0;
+ int next_cap_dev_start_slot = 0;
+ struct i2s_device *dev = NULL;
+
+ for (i = 0; i < MAX_I2S_DEVICES; i++) {
+ dev = ai->i2s_devices[i];
+ if (dev != NULL && dev->direction == in) {
+ if (next_cap_dev_start_slot > ai->i2s_word_length) {
+ LOG_ERROR("Total sample-length of capture "
+ "devices on audio-interface %d > "
+ "i2s word-length", ai->id);
+ return -1;
+ }
+ dev->tsl_slot = next_cap_dev_start_slot;
+ next_cap_dev_start_slot += dev->sample_length;
+ }
+ if (dev != NULL && dev->direction == out) {
+ if (next_pbk_dev_start_slot > ai->i2s_word_length) {
+ LOG_ERROR("Total sample-length of playback "
+ "devices on audio-interface %d > "
+ "i2s word-length", ai->id);
+ return -1;
+ }
+ dev->tsl_slot = next_pbk_dev_start_slot;
+ next_pbk_dev_start_slot += dev->sample_length;
+ }
+ }
+ return 0;
+}
+
+static void tsl_prepare_capture_slot(struct i2s_device *dev,
+ uint32_t tsl[],
+ int slot,
+ int *total_bytes_cap)
+{
+ int word_length = dev->audio_interface->i2s_word_length;
+
+ if (slot%word_length >= dev->tsl_slot &&
+ slot%word_length < (dev->tsl_slot + dev->sample_length)) {
+ setbits(&tsl[slot], TSL_DIS_Ax[dev->audio_interface->id], 2,
+ TSL_SDx[dev->sd]);
+ setbits(&tsl[slot], TSL_SDW_Ax[dev->audio_interface->id], 1, 1);
+ setbits(&tsl[slot], TSL_SIB_Ax[dev->audio_interface->id], 1,
+ (dev->hw_mon == source ? 1 : 0));
+ (*total_bytes_cap)++;
+ setbits(&tsl[slot], TSL_SF_Ax[dev->audio_interface->id], 1,
+ (*total_bytes_cap%DWBUF_SIZE == 0 ? 1 : 0));
+ }
+}
+
+static void tsl_prepare_playback_slot(struct i2s_device *dev,
+ uint32_t tsl[],
+ int slot,
+ int *total_bytes_pbk,
+ int *bsel)
+{
+ unsigned int tmp = 0;
+ int ws_line = 0;
+ int bsel_offset = 0;
+ int word_length = dev->audio_interface->i2s_word_length;
+
+ if (slot%word_length >= dev->tsl_slot &&
+ slot%word_length < (dev->tsl_slot + dev->sample_length)) {
+ bsel_offset = (dev->hw_mon == destination ? 4 : 0);
+ /* see documentation in wiki for details regarding endianness */
+ setbits(&tsl[slot], TSL_BSEL_Ax[dev->audio_interface->id], 3,
+ (dev->sample_length == 2 ?
+ (dev->endian == le ? (1 - *bsel) & 3 : *bsel) :
+ (dev->endian == le ? 3 - *bsel : *bsel))
+ + bsel_offset);
+ setbits(&tsl[slot], TSL_DOD_Ax[dev->audio_interface->id], 2,
+ TSL_SDx[dev->sd]);
+ setbits(&tsl[slot], TSL_LF_Ax[dev->audio_interface->id], 1,
+ (*total_bytes_pbk%DWBUF_SIZE == 0 ? 1 : 0));
+ (*total_bytes_pbk)++;
+ *bsel = (*bsel + 1)%DWBUF_SIZE;
+ }
+ /* WS: drive high until dev->tsl_slot, then drive low for word-
+ length slots, then drive high again */
+ tmp = ((unsigned int)slot) - dev->tsl_slot;
+ ws_line = (tmp%(2 * word_length) < word_length ? 0 : 1);
+ setbits(&tsl[slot], TSL_WSx[dev->ws], 1, ws_line);
+}
+
+
+/**
+ * Set bits at a given offset to the given value.
+ * @param reg The variable where the bits are set
+ * @param offset The offset where the bits are set
+ * @param len The number of contiguous bits
+ * @param val The value to be set
+ */
+static void setbits(uint32_t *reg,
+ unsigned int offset,
+ unsigned int len,
+ uint32_t val)
+{
+ *reg &= ~(((1 << len) - 1) << offset); /* clear bits (!) */
+ *reg |= (val << offset);
+}
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2s.h
../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2s.h
--- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/saa7146i2s.h 1970-01-01 01:00:00.000000000 +0100
+++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/saa7146i2s.h 2008-10-22 23:34:05.000000000 +0200
@@ -0,0 +1,197 @@
+/*
+ * SAA7146 I2S and audio-interface abstraction layer
+ * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef SAA7146I2S_H_
+#define SAA7146I2S_H_
+
+#include <linux/types.h>
+#include "saa7146.h"
+
+#define MAX_I2S_DEVICES 5 /* SAA4146 supports a max. of 5 i2s devices */
+#define MAX_AUDIO_INTERFACES 2 /* SAA7146 has 2 independent audio-interfaces */
+
+/* forward declarations due to mutual reference audio_interface<->i2s_device */
+struct audio_interface;
+
+/**
+ * Enumeration of SAA7146 audio-interfaces A1 and A2.
+ * Order matters (see head of saa7146i2s.c: address and bit offset maps)
+ */
+enum audio_interfaces {a1, a2};
+
+/**
+ * Enumeration of SAA7146 i2c WS lines.
+ * Order matters (see head of saa7146i2s.c: address and bit offset maps)
+ */
+enum ws_lines {ws0, ws1, ws2, ws3, ws4};
+
+/**
+ * Enumeration of SAA7146 i2c SD lines.
+ * sd0_i_a2: SD0 is always input on SAA7146 audiointerface A2
+ * sd0_o_a1: SD0 is always output on SAA7146 audiointerface A1
+ * sd4_i_a1: SD4 is always input on SAA7146 audiointerface A1
+ * sd4_o_a2: SD4 is always output on SAA7146 audiointerface A2
+ * Order matters (see head of saa7146i2s.c: address and bit offset maps)
+ */
+enum sd_lines {sd0_i_a2, sd0_o_a1, sd1_io_ax, sd2_io_ax, sd3_io_ax, sd4_i_a1,
+ sd4_o_a2};
+
+enum endian {le, be};
+
+enum directions {in, out};
+
+enum states {disabled, enabled};
+
+enum hw_mon {source, destination, notmonitored};
+
+/**
+ * tsl_slot: SAA7146 TSL slot-number of 1st channel MSB
+ * audio_interface: convenience reference
+ * sample_length: sample length in bytes
+ */
+struct i2s_device {
+ enum ws_lines ws;
+ enum sd_lines sd;
+ unsigned int tsl_slot;
+ struct audio_interface *audio_interface;
+ int sample_length;
+ enum endian endian;
+ enum states state;
+ enum directions direction;
+ enum hw_mon hw_mon;
+};
+
+/**
+ * i2s_word_length: word length in bytes
+ * i2s_superframe_length: superframe length in bytes
+ * fs: each audio-interface can have its own samplerate
+ */
+struct audio_interface {
+ enum audio_interfaces id;
+ int i2s_word_length;
+ int i2s_superframe_length;
+ int fs;
+ struct i2s_device *i2s_devices[MAX_I2S_DEVICES];
+};
+
+/**
+ * struct audio_interface and struct i2c_device instances are statically
+ * allocated and are 'owned' by this struct saa7146i2s -> this is the place
+ * where the actual data structures are defined (no dynamic allocation
+ * elsewhere).
+ */
+struct saa7146i2s {
+ struct saa7146reg chip;
+ struct audio_interface audio_interfaces[MAX_AUDIO_INTERFACES];
+ struct i2s_device i2s_devices[MAX_I2S_DEVICES];
+ uint32_t acon1;
+};
+
+/**
+ * Initialize an SAA7146's audio-interface. The parameter values depend on the
+ * hardware setup. As a rule, these are rather static settings and are
+ * established once in the module's lifecycle.
+ * @param chipi2s used for I2S device management and SAA7146 register access,
+ * must not be NULL.
+ * @param audio_interface_id the SAA7146 audio-interface to be initialized.
+ * @param i2s_word_length word-length of i2s devices operated by the given
+ * audio-interface. One audio-interface can handle more than one i2s device,
+ * but the word-length (i.e. 1/2 WS-period) must be the same for all.
+ * word-length 1, 2 and 4 bytes are supported.
+ * @param i2s_superframe_length a superframe contains a number of WS-periods.
+ * The following condition must match:
+ *
+ * (MIN_SUPERFRAME_LENGTH <= superframe-length <= MAX_SUPERFRAME_LENGTH)
+ * AND superframe-length == [2,4,8,16] * word-length
+ *
+ * @return 0 in case of success or -1 in case of failure.
+ */
+int saa7146_i2s_init_audio_interface(struct saa7146i2s *chipi2s,
+ enum audio_interfaces audio_interface_id,
+ int i2s_word_length,
+ int i2s_superframe_length);
+
+/**
+ * TODO: description
+ */
+void saa7146_i2s_enable_audio_interface(struct saa7146i2s *chipi2s,
+ struct audio_interface *ai);
+
+/**
+ * TODO: description
+ */
+void saa7146_i2s_disable_audio_interface(struct saa7146i2s *chipi2s,
+ struct audio_interface *ai);
+
+/**
+ * Initializes an i2s device. As a rule, init values are rather static, e.g.
+ * given by the HW wiring, and are established once in the module's lifecycle.
+ * WS determines the order in which the i2s devices (= stereo audio channels)
+ * are expected to appear in the host memory.
+ * @param chipi2s used for I2S device management and SAA7146 register access,
+ * must not be NULL.
+ * @param audio_interface_id the SAA7146 audio-interface which operates this
+ * i2s device.
+ * @param direction either 'in' or 'out'.
+ * @param ws the WS line to be used for this i2s device.
+ * @param sd the SD line to be used for this i2s device.
+ * @return 0 in case of success or -1 in case of failure.
+ */
+int saa7146_i2s_init_device(struct saa7146i2s *chipi2s,
+ enum audio_interfaces audio_interface_id,
+ enum directions direction,
+ enum ws_lines ws,
+ enum sd_lines sd);
+
+/**
+ * Enables an i2s device for capture or playback.
+ * @param chipi2s used for I2S device management and SAA7146 register access,
+ * must not be NULL.
+ * @param device A reference to the i2s_device reference returned by
+ * saa7146_i2s_init_device(), must not be NULL.
+ * @param sample_length the length of samples transferred over this i2s device.
+ * @param endian endianess of the captured bytes in the host memory.
+ * @return 0 for success or -1 for failure.
+ */
+int saa7146_i2s_enable_device(struct saa7146i2s *chipi2s,
+ struct i2s_device *device,
+ int sample_length,
+ enum endian endian);
+
+/**
+ * Disables an i2s device.
+ * @param chipi2s used for I2S device management and SAA7146 register access,
+ * must not be NULL.
+ * @param device A reference to the i2s_device reference returned by
+ * saa7146_i2s_init_device(), must not be NULL.
+ * @return 0 for success or -1 for failure.
+ */
+int saa7146_i2s_disable_device(struct saa7146i2s *chipi2s,
+ struct i2s_device *device);
+
+/**
+ * @return 1 if the given value is a power of 2.
+ */
+static inline int is_pow_of_2(int val)
+{
+ return ((val != 0) && ((val & (val - 1)) == 0));
+}
+
+#endif /*SAA7146I2S_H_*/
2
1
Dear All,
I have some doubts on the ALSA programming,
1)My ALSA driver supports minimum Period Bytes size as 32 bytes, but when i
set the period size as 512 bytes it is always setting to 1024 bytes.I am
using the function "snd_pcm_hw_params_set_period_size_near()" . What might
be wrong here?Is the function what i am using correct?
2)How the Library functions and the Driver functions interact, what is the
interface between the library functions and driver functions?Is there any
document which explains this. I got a document which explains how to write
ALSA driver.But it does not mention the interface with the library.
3)How Can we achive MIC and LINEIN switching using the mixer?What library
function do i need to use to achive this?
4)If i want to get the Timestamp for each buffer captured, what library
function should be used?
5)I am trying to capture and play the same audio (ALSA duplex). When i
capture and store it in a file and play from the file i am able to hear the
sound properly. If i capture in a buffer and play the same buffer directly
it says Broken PIPE ERROR in the playout.
I tried the capture and playout in two different threads, i am able to hear
the sound and i am not getting any Broken PIPE error. But along with the
audio i am hearing some noise.
Do we need to use some kind of synchronisation between the threads for
capture and playout?
Any help on these things is appreciated.
Thank you in advance.
Regards,
santosh
1
0
Greetings,
running aplay -v will show the plugin chain setup.
/proc/asound/card/pcm/* shows hw and sw params for the card driver when
in use by any app
Is there any way to show the alsa-lib plugin settings a-la aplay for an
arbitrary application?
thanks
--
Eliot
3
7
[alsa-devel] Fwd: multiseat and alsa audio systems (usb headsets and usb audio adapters)
by Sean McNamara 22 Oct '08
by Sean McNamara 22 Oct '08
22 Oct '08
Oops! Forgot to include the ML on this reply. Maybe someone else will
find this info useful, or be able to add constructive comments to what
has been said :)
Sean
---------- Forwarded message ----------
From: Sean McNamara <smcnam(a)gmail.com>
Date: Wed, Oct 22, 2008 at 4:17 PM
Subject: Re: [alsa-devel] multiseat and alsa audio systems (usb
headsets and usb audio adapters)
To: Jelle de Jong <jelledejong(a)powercraft.nl>
On Wed, Oct 22, 2008 at 1:48 PM, Jelle de Jong
<jelledejong(a)powercraft.nl> wrote:
> Hello everybody,
>
> I have a four to eighth muliseat debian linux system here and it is
> missing sound for the users. I was hoping the alsa team can help me with
> this issue.
All sound issues on Linux are extremely application-specific. Getting
sound to work without any specific application in mind is asking for
trouble, because there is no single configuration of the sound stack
(which, if you expand into historical sound APIs and servers, contains
about 20 different ways to play sound) that will satisfy any
application without further configuration.
Since this is not a really well-formed question, I will proceed with
the assumption that you want to be able to run, for example,
aplay /usr/share/sounds/pop.wav
in the console, and have it produce sound in the "appropriate"
headset. Here's how you proceed:
1. Each person on the multiseat box must have their own UNIX user account.
2. Each user account must have an ~/.asoundrc file.
3. Each ~/.asoundrc file must redefine the "default" device. How you
redefine it depends on a lot of things:
(a) Do you want _direct_ hardware access to the sound card, i.e. one
application at a time?
(b) Do you want to use ALSA's built-in software mixing, i.e. dmix?
(c) How many channels?
(d) Do you want to use PulseAudio?
(e) Do you want to use JACK?
(f) For each USB headset, what is its corresponding "raw device
string"? This will be something like hw:n, where n is a number
starting from 0, depending on the order in which the USB drivers
initialize each USB controller.
All of the above questions, and others, will determine exactly how
each user configures their ~/.asoundrc file.
The *extremely naive* (not recommended) example of an ~/.asoundrc is like:
pcm.!default {
card 3
}
to have ALSA clients play sound, by default, to the fourth sound
device that happened to be initialized.
>
> I have usb headsets and usb audio devices and all users have there own
> usb hub. There are no internal audio cards in the multiseat server.
>
> How can we configure an system that the user can listen to audio only to
> the devices connected to his usb hub.
Well, this is guaranteed by design, isn't it? Without physical access
to someone else's headset, how would I get access to the audio streams
being played to that headset? I don't think this really expresses what
your problem is.
If we flip your question around, it might be a little more
interesting: "How can we configure a system so that each user's
applications will only play audio to that user's corresponding
headset?"
You can interpret this question in two ways:
1. "How can we _prevent_ users from playing sound to each other's
sound cards?" [mutually untrust[ed/ing] users]
or
2. "How can we configure the default settings so that, if untampered
with, a user's applications will play sound to that user's own headset
and not anyone else's?" [mutually trust[ed/ing] users]
The lack of hardware mixing actually comes back to being a security
benefit to user space control in this kind of situation. Recall that
if I run
aplay --device=hw:0 /usr/share/sounds/pop.wav
then as long as the aplay program is playing sound to hw:0, it is
*impossible* (short of some really devious hacking in alsa-lib or the
kernel) for another application -- from this user, or another -- to
also play sound to hw:0 at the same time. I'll call this claim "No
Native SW Mixing".
Now, let's look at what is required to satisfy the question in each of
the two above cases.
Trusted:
1. We know that users aren't going to maliciously try and hurt one
another's ears by setting someone else's mixer really loud and playing
static to them.
2. We know that users won't try to tamper with eachother's processes
or configuration files; once things are configured correctly, it'll
stay that way.
3. By 'No Native SW Mixing', we know that users can *theoretically*
hog another user's sound device whenever that user has zero
applications currently using it.
4. Since users trust each other, they won't do this.
5. We can use something like dmix, or just use hw:0 if users don't
need simultaneous app output, and things will be fine.
6. dmix is a viable option to implement software mixing.
7. PulseAudio is also a viable option to implement software mixing.
Untrusted:
1. Users may want to maliciously try and hurt one another's ears by
setting someone else's mixer really loud and playing static to them.
2. Users may try to tamper with eachother's processes or configuration
files; once things are configured correctly, it could get messed up
again. [There's not much you can do to prevent this ... at least not
based on existing open source tools.]
3. By 'No Native SW Mixing', we know that users can *theoretically*
hog another user's sound device whenever that user has zero
applications currently using it.
4. Since users do not trust each other, there is potential for 3 to happen.
5. We must use 'No Native SW Mixing' to our advantage, to create a
walled garden around each user's sound experience. We need a process
that, once started by a user, will permanently "hog" the sound card
that user wants to use. This process should, ideally, allow other
processes run by that user (and by that user only) to access their USB
headset for output or input. We need a "gatekeeper" that is sensitive
to the context in which an app is being run.
6. dmix is a NOT viable option to implement software mixing, because:
(a) Once all dmix applications close their streams, the audio device
hw:n is available, and some malicious user can decide to acquire it,
abuse it, and prevent its rightful user from accessing it with his own
applications.
(b) dmix does not restrict users from outputting sound based on their
UID or session. It is NOT a gatekeeper, just an open gate.
7. PulseAudio is [perhaps the best] viable solution to implement
software mixing for each respective user. It is also a viable solution
to preventing users from taking control of one another's sound
devices.
Configuring pulseaudio for a multi-soundcard multi-user system is out
of the scope of this mailing list, but suffice it to say that it can
be done. You will want to run one PulseAudio daemon per user, and each
daemon will hold exclusive control over exactly one USB headset. Each
daemon will only accept clients' requests for audio I/O from the user
who started the daemon. Do make sure that you disable
module-suspend-on-idle, or PulseAudio will actually give up the sound
device after a period of inactivity, making it insecure in an
untrusted environment.
BTW, if this project is for a commercial interest, you usually pay
someone for this kind of in-depth analysis. If you're that someone,
you might want to read up on some of the underlying technologies so
that you can develop this sort of reasoning on your own. I can
envision a very real situation for both the trusted and the untrusted
environment, so there is definitely a demand for this kind of
specialist. Trusted environments are popular at workplaces, and
untrusted multi-seat boxes are popular at computer labs, public
libraries, etc.
>
> Any help is really appreciated.
Please let me know if this information was helpful -- it will help me
gauge, in turn, whether I should spend my time writing up these sorts
of answers on the ML. FWIW, I searched around for existing articles
touching this subject, but I couldn't find anyone who addresses this
specific issue accurately or in enough detail.
Sean
P.S. - I once spoke to a user on IRC who was having intermittent
problems with multiple USB headsets. It turns out that the USB
controllers in the computer didn't have enough bandwidth, or power (or
both) to allow all of the headsets to play at once. If, after
configuring, you run into issues where only so many users can play
sound at once, then you should invest in additional PCI or
(preferably) PCI-Express USB controllers. Note that adding hubs
upstream of a USB controller does absolutely nothing to lessen the
load on the USB controllers that are integrated into your motherboard.
>
> Thank in advance,
>
> Jelle
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel(a)alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
1
0
[alsa-devel] multiseat and alsa audio systems (usb headsets and usb audio adapters)
by Jelle de Jong 22 Oct '08
by Jelle de Jong 22 Oct '08
22 Oct '08
Hello everybody,
I have a four to eighth muliseat debian linux system here and it is
missing sound for the users. I was hoping the alsa team can help me with
this issue.
I have usb headsets and usb audio devices and all users have there own
usb hub. There are no internal audio cards in the multiseat server.
How can we configure an system that the user can listen to audio only to
the devices connected to his usb hub.
Any help is really appreciated.
Thank in advance,
Jelle
1
0
At Tue, 21 Oct 2008 14:46:19 +0200,
Nir Tzachar wrote:
>
> >> The bad news are that mplayer misbehaves and will get stuck every time
> >> I try to change the volume. However, this does not happen in vlc. Any
> >> ideas?
> >
> > Well, we need to sort out where the problem comes from.
> > Try the vanilla Linus tree without sound-2.6 whether the problem
> > persists.
>
> I'll try to remove the sound-2.6 tree and report.
Nir, do you have any results?
I tried to reproduce the problem by myself, but I couldn't.
So I wonder whether it's a codec specific or I missed something...
thanks,
Takashi
2
1