first release
authora0220410 <peter-li@ti.com>
Sat, 26 Nov 2016 00:22:50 +0000 (08:22 +0800)
committera0220410 <peter-li@ti.com>
Sat, 26 Nov 2016 00:22:50 +0000 (08:22 +0800)
15 files changed:
Kconfig [new file with mode: 0755]
License text GPLv2.txt [new file with mode: 0755]
Makefile [new file with mode: 0755]
dai_link.readme [new file with mode: 0755]
dts.readme [new file with mode: 0755]
tas2557-codec.c [new file with mode: 0755]
tas2557-codec.h [new file with mode: 0755]
tas2557-core.c [new file with mode: 0755]
tas2557-core.h [new file with mode: 0755]
tas2557-misc.c [new file with mode: 0755]
tas2557-misc.h [new file with mode: 0755]
tas2557-regmap.c [new file with mode: 0755]
tas2557.h [new file with mode: 0755]
tiload.c [new file with mode: 0755]
tiload.h [new file with mode: 0755]

diff --git a/Kconfig b/Kconfig
new file mode 100755 (executable)
index 0000000..2f49493
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,15 @@
+
+menuconfig SND_SOC_TAS2557
+       tristate "Texas Instruments TAS2557 SmartAmp(R)"
+
+if SND_SOC_TAS2557
+config TAS2557_REGMAP
+    bool "Use of RegMap API"
+
+config TAS2557_CODEC
+    bool "Codec Driver support"
+
+config TAS2557_MISC
+    bool "Misc Driver support"
+       
+endif # SND_SOC_TAS2557
diff --git a/License text GPLv2.txt b/License text GPLv2.txt
new file mode 100755 (executable)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <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 Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100755 (executable)
index 0000000..4448b3a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+snd-soc-tas2557-objs := tas2557-core.o tas2557-regmap.o tas2557-codec.o tas2557-misc.o tiload.o
+obj-$(CONFIG_SND_SOC_TAS2557) += snd-soc-tas2557.o
\ No newline at end of file
diff --git a/dai_link.readme b/dai_link.readme
new file mode 100755 (executable)
index 0000000..8ca1daa
--- /dev/null
@@ -0,0 +1,28 @@
+example for connecting CPU DAI\r
+\r
+#if defined(CONFIG_TAS2557_CODEC)      \r
+       {\r
+               .name = LPASS_BE_PRI_MI2S_RX,\r
+               .stream_name = "Primary MI2S Hostless Playback",\r
+               .cpu_dai_name = "msm-dai-q6-mi2s.0",\r
+               .platform_name = "msm-pcm-routing",\r
+               .codec_name     = "tas2557.3-004f",\r
+               .codec_dai_name = "tas2557 ASI1",\r
+               .no_pcm = 1,\r
+               .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,\r
+               .be_hw_params_fixup = msm_be_prim_mi2s_hw_params_fixup,\r
+               .ops = &msm8974_prim_mi2s_be_ops,\r
+       },\r
+       {\r
+               .name = LPASS_BE_PRI_MI2S_TX,\r
+               .stream_name = "Primary MI2S Hostless Capture",\r
+               .cpu_dai_name = "msm-dai-q6-mi2s.0",\r
+               .platform_name = "msm-pcm-routing",\r
+               .codec_name     = "tas2557.3-004f",\r
+               .codec_dai_name = "tas2557 ASI1",\r
+               .no_pcm = 1,\r
+               .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,\r
+               .be_hw_params_fixup = msm_be_prim_mi2s_hw_params_fixup,\r
+               .ops = &msm8974_prim_mi2s_be_ops,\r
+       },\r
+#endif 
\ No newline at end of file
diff --git a/dts.readme b/dts.readme
new file mode 100755 (executable)
index 0000000..2a9a377
--- /dev/null
@@ -0,0 +1,9 @@
+example for dts:
+               
+               tas2557@4c {
+                       compatible = "ti,tas2557";
+                       reg = <0x4c>;                   
+                       ti,cdc-reset-gpio = <&msmgpio 73 0>;
+                       ti,load = <0>;   /* 0, 8Ohm; 1, 6Ohm; 2, 4Ohm */
+                       status = "ok";
+               };
\ No newline at end of file
diff --git a/tas2557-codec.c b/tas2557-codec.c
new file mode 100755 (executable)
index 0000000..d172f4b
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-codec.c
+**
+** Description:
+**     ALSA SoC driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_CODEC
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2557-core.h"
+#include "tas2557-codec.h"
+
+#undef KCONTROL_CODEC
+
+static struct tas2557_register register_addr = { 0 };
+
+#define TAS2557_REG_IS_VALID(book, page, reg) \
+        ((book >= 0) && (book <= 255) &&\
+        (page >= 0) && (page <= 255) &&\
+        (reg >= 0) && (reg <= 127))
+
+static unsigned int tas2557_codec_read(struct snd_soc_codec *pCodec,
+       unsigned int nRegister)
+{
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+       int ret = 0;
+       unsigned int Value = 0;
+
+       mutex_lock(&pTAS2557->codec_lock);
+       ret = pTAS2557->read(pTAS2557, nRegister, &Value);
+       mutex_unlock(&pTAS2557->codec_lock);
+       
+       if (ret < 0) {
+               dev_err(pTAS2557->dev, "%s, %d, ERROR happen=%d\n", __FUNCTION__,
+                       __LINE__, ret);
+               return 0;
+       } else
+               return Value;
+}
+
+static int tas2557_codec_write(struct snd_soc_codec *pCodec, unsigned int nRegister,
+       unsigned int nValue)
+{
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+       int ret = 0;
+       mutex_lock(&pTAS2557->codec_lock);
+       ret = pTAS2557->write(pTAS2557, nRegister, nValue);
+       mutex_unlock(&pTAS2557->codec_lock);
+       
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget tas2557_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("ASI2", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("ASIM", "ASIM Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_OUT_DRV("ClassD", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("NDivider", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas2557_audio_map[] = {
+       {"DAC", NULL, "ASI1"},
+       {"DAC", NULL, "ASI2"},
+       {"DAC", NULL, "ASIM"},
+       {"ClassD", NULL, "DAC"},
+       {"OUT", NULL, "ClassD"},
+       {"DAC", NULL, "PLL"},
+       {"DAC", NULL, "NDivider"},
+};
+
+static int tas2557_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       
+       dev_dbg(pTAS2557->dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static void tas2557_shutdown(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(pTAS2557->dev, "%s\n", __func__);
+}
+
+static int tas2557_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       
+       dev_dbg(pTAS2557->dev, "%s\n", __func__);
+       mutex_lock(&pTAS2557->codec_lock);
+       tas2557_enable(pTAS2557, !mute);
+       mutex_unlock(&pTAS2557->codec_lock);
+       
+       return 0;
+}
+
+static int tas2557_set_dai_sysclk(struct snd_soc_dai *pDAI,
+       int nClkID, unsigned int nFreqency, int nDir)
+{
+       struct snd_soc_codec *pCodec = pDAI->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+       dev_dbg(pTAS2557->dev, "tas2557_set_dai_sysclk: freq = %u\n", nFreqency);
+
+       return 0;
+}
+
+static int tas2557_hw_params(struct snd_pcm_substream *pSubstream,
+       struct snd_pcm_hw_params *pParams, struct snd_soc_dai *pDAI)
+{
+       struct snd_soc_codec *pCodec = pDAI->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+       dev_dbg(pCodec->dev, "%s\n", __func__);
+       
+       mutex_lock(&pTAS2557->codec_lock);
+       tas2557_set_sampling_rate(pTAS2557, params_rate(pParams));
+       mutex_unlock(&pTAS2557->codec_lock);
+
+       return 0;
+}
+
+static int tas2557_set_dai_fmt(struct snd_soc_dai *pDAI, unsigned int nFormat)
+{
+       struct snd_soc_codec *codec = pDAI->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       
+       dev_dbg(pTAS2557->dev, "%s\n", __func__);
+       
+       return 0;
+}
+
+static int tas2557_prepare(struct snd_pcm_substream *pSubstream,
+       struct snd_soc_dai *pDAI)
+{
+       struct snd_soc_codec *codec = pDAI->codec;
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       
+       dev_dbg(pTAS2557->dev, "%s\n", __func__);
+       
+       return 0;
+}
+
+static int tas2557_set_bias_level(struct snd_soc_codec *pCodec,
+       enum snd_soc_bias_level eLevel)
+{
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+       
+       dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, eLevel);
+
+       return 0;
+}
+
+static int tas2557_codec_probe(struct snd_soc_codec *pCodec)
+{
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+       dev_info(pTAS2557->dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static int tas2557_codec_remove(struct snd_soc_codec *pCodec)
+{
+       return 0;
+}
+
+static int tas2557_get_reg_addr(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pUcontrol)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       pUcontrol->value.integer.value[0] = register_addr.book;
+       pUcontrol->value.integer.value[1] = register_addr.page;
+       pUcontrol->value.integer.value[2] = register_addr.reg;
+
+       dev_dbg(pTAS2557->dev, "%s: Get address [%d, %d, %d]\n", __func__,
+               register_addr.book, register_addr.page, register_addr.reg);
+
+       return 0;
+}
+
+static int tas2557_put_reg_addr(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pUcontrol)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       register_addr.book = pUcontrol->value.integer.value[0];
+       register_addr.page = pUcontrol->value.integer.value[1];
+       register_addr.reg = pUcontrol->value.integer.value[2];
+
+       dev_dbg(pTAS2557->dev, "%s: Set address [%d, %d, %d]\n", __func__,
+               register_addr.book, register_addr.page, register_addr.reg);
+
+       return 0;
+}
+
+static int tas2557_get_reg_value(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pUcontrol)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg;
+       unsigned int nValue;
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       
+       if (TAS2557_REG_IS_VALID(register_addr.book,
+                       register_addr.page, register_addr.reg)) {
+               reg = TAS2557_REG((unsigned int) register_addr.book,
+                       (unsigned int) register_addr.page,
+                       (unsigned int) register_addr.reg);
+               pTAS2557->read(pTAS2557, reg, &nValue);
+               pUcontrol->value.integer.value[0] = nValue;
+       } else {
+               dev_err(pTAS2557->dev, "%s: Invalid register address!\n", __func__);
+               pUcontrol->value.integer.value[0] = 0xFFFF;
+       }
+
+       dev_dbg(pTAS2557->dev, "%s: Read [%d, %d, %d] = %ld\n", __func__,
+               register_addr.book, register_addr.page, register_addr.reg,
+               pUcontrol->value.integer.value[0]);
+               
+       mutex_unlock(&pTAS2557->codec_lock);
+       return 0;
+}
+
+static int tas2557_put_reg_value(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pUcontrol)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg;
+
+       mutex_lock(&pTAS2557->codec_lock);
+       
+       if (TAS2557_REG_IS_VALID(register_addr.book,
+                       register_addr.page, register_addr.reg)) {
+               reg = TAS2557_REG((unsigned int) register_addr.book,
+                       (unsigned int) register_addr.page,
+                       (unsigned int) register_addr.reg);
+               pTAS2557->write(pTAS2557, reg, pUcontrol->value.integer.value[0]);
+       } else {
+               dev_err(pTAS2557->dev, "%s: Invalid register address!\n", __func__);
+       }
+
+       dev_dbg(pTAS2557->dev, "%s: Write [%d, %d, %d] = %ld\n", __func__,
+               register_addr.book, register_addr.page, register_addr.reg,
+               pUcontrol->value.integer.value[0]);
+
+       mutex_unlock(&pTAS2557->codec_lock);                    
+       return 0;
+}
+
+static int tas2557_power_ctrl_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       
+       pValue->value.integer.value[0] = pTAS2557->mnPowerCtrl;
+       dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_get = %d\n",
+               pTAS2557->mnPowerCtrl);
+               
+       mutex_unlock(&pTAS2557->codec_lock);                    
+       return 0;
+}
+
+static int tas2557_power_ctrl_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       pTAS2557->mnPowerCtrl = pValue->value.integer.value[0];
+
+       dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_put = %d\n",
+               pTAS2557->mnPowerCtrl);
+
+       if (pTAS2557->mnPowerCtrl == 1)
+               tas2557_enable(pTAS2557, true);
+       if (pTAS2557->mnPowerCtrl == 0)
+               tas2557_enable(pTAS2557, false);
+               
+       mutex_unlock(&pTAS2557->codec_lock);
+       return 0;
+}
+
+static int tas2557_fs_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       int nFS = 48000;
+
+       mutex_lock(&pTAS2557->codec_lock);
+       
+       if (pTAS2557->mpFirmware->mnConfigurations)
+               nFS = pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration].mnSamplingRate;
+       
+       pValue->value.integer.value[0] = nFS;
+       
+       mutex_unlock(&pTAS2557->codec_lock);    
+       
+       dev_info(pTAS2557->dev, "tas2557_fs_get = %d\n", nFS);
+       return 0;
+}
+
+static int tas2557_fs_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+       int nFS = pValue->value.integer.value[0];
+       
+       dev_info(pTAS2557->dev, "tas2557_fs_put = %d\n", nFS);
+       
+       mutex_lock(&pTAS2557->codec_lock);      
+       ret = tas2557_set_sampling_rate(pTAS2557, nFS);
+       
+       mutex_unlock(&pTAS2557->codec_lock);    
+       return ret;
+}
+
+static int tas2557_program_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       mutex_lock(&pTAS2557->codec_lock);      
+       pValue->value.integer.value[0] = pTAS2557->mnCurrentProgram;
+       dev_dbg(pTAS2557->dev, "tas2557_program_get = %d\n",
+               pTAS2557->mnCurrentProgram);
+       mutex_unlock(&pTAS2557->codec_lock);                    
+       return 0;
+}
+
+static int tas2557_program_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nProgram = pValue->value.integer.value[0];
+       int ret = 0;
+       mutex_lock(&pTAS2557->codec_lock);      
+       ret = tas2557_set_program(pTAS2557, nProgram);
+       mutex_unlock(&pTAS2557->codec_lock);    
+       return ret;
+}
+
+static int tas2557_configuration_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       pValue->value.integer.value[0] = pTAS2557->mnCurrentConfiguration;
+       dev_dbg(pTAS2557->dev, "tas2557_configuration_get = %d\n",
+               pTAS2557->mnCurrentConfiguration);
+       mutex_unlock(&pTAS2557->codec_lock);                    
+       return 0;
+}
+
+static int tas2557_configuration_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nConfiguration = pValue->value.integer.value[0];
+       int ret = 0;
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       ret = tas2557_set_config(pTAS2557, nConfiguration);
+       mutex_unlock(&pTAS2557->codec_lock);    
+       return ret;
+}
+
+static int tas2557_calibration_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2557->codec_lock);      
+       pValue->value.integer.value[0] = pTAS2557->mnCurrentCalibration;
+       dev_info(pTAS2557->dev,
+               "tas2557_calibration_get = %d\n",
+               pTAS2557->mnCurrentCalibration);
+       mutex_unlock(&pTAS2557->codec_lock);                    
+       return 0;
+}
+
+static int tas2557_calibration_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nCalibration = pValue->value.integer.value[0];
+       int ret = 0;
+
+       mutex_lock(&pTAS2557->codec_lock);              
+       ret = tas2557_set_calibration(pTAS2557, nCalibration);
+       mutex_unlock(&pTAS2557->codec_lock);    
+       
+       return ret;
+}
+
+/*
+ * DAC digital volumes. From 0 to 15 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, 0, 100, 0);
+
+static const struct snd_kcontrol_new tas2557_snd_controls[] = {
+       SOC_SINGLE_TLV("DAC Playback Volume", TAS2557_SPK_CTRL_REG, 3, 0x0f, 0,
+               dac_tlv),
+       SOC_SINGLE_MULTI_EXT("Reg Addr", 0, 0, INT_MAX, 0, 3, tas2557_get_reg_addr,
+               tas2557_put_reg_addr),
+       SOC_SINGLE_EXT("Reg Value", SND_SOC_NOPM, 0, 0xFFFF, 0,
+               tas2557_get_reg_value, tas2557_put_reg_value),
+       SOC_SINGLE_EXT("PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0,
+               tas2557_power_ctrl_get, tas2557_power_ctrl_put),
+       SOC_SINGLE_EXT("Program", SND_SOC_NOPM, 0, 0x00FF, 0, tas2557_program_get,
+               tas2557_program_put),
+       SOC_SINGLE_EXT("Configuration", SND_SOC_NOPM, 0, 0x00FF, 0,
+               tas2557_configuration_get, tas2557_configuration_put),
+       SOC_SINGLE_EXT("FS", SND_SOC_NOPM, 8000, 48000, 0,
+               tas2557_fs_get, tas2557_fs_put),
+       SOC_SINGLE_EXT("Calibration", SND_SOC_NOPM, 0, 0x00FF, 0,
+               tas2557_calibration_get, tas2557_calibration_put),
+};
+
+static struct snd_soc_codec_driver soc_codec_driver_tas2557 = {
+       .probe = tas2557_codec_probe,
+       .remove = tas2557_codec_remove,
+       .read = tas2557_codec_read,
+       .write = tas2557_codec_write,
+       .set_bias_level = tas2557_set_bias_level,
+       .idle_bias_off = true,
+       //.ignore_pmdown_time = true,
+       .controls = tas2557_snd_controls,
+       .num_controls = ARRAY_SIZE(tas2557_snd_controls),
+       .dapm_widgets = tas2557_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tas2557_dapm_widgets),
+       .dapm_routes = tas2557_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(tas2557_audio_map),
+};
+
+static struct snd_soc_dai_ops tas2557_dai_ops = {
+       .startup = tas2557_startup,
+       .shutdown = tas2557_shutdown,
+       .digital_mute = tas2557_mute,
+       .hw_params = tas2557_hw_params,
+       .prepare = tas2557_prepare,
+       .set_sysclk = tas2557_set_dai_sysclk,
+       .set_fmt = tas2557_set_dai_fmt,
+};
+
+#define TAS2557_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+             SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static struct snd_soc_dai_driver tas2557_dai_driver[] = {
+       {
+               .name = "tas2557 ASI1",
+               .id = 0,
+               .playback = {
+                               .stream_name = "ASI1 Playback",
+                               .channels_min = 2,
+                               .channels_max = 2,
+                               .rates = SNDRV_PCM_RATE_8000_192000,
+                               .formats = TAS2557_FORMATS,
+                       },
+               .ops = &tas2557_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "tas2557 ASI2",
+               .id = 1,
+               .playback = {
+                               .stream_name = "ASI2 Playback",
+                               .channels_min = 2,
+                               .channels_max = 2,
+                               .rates = SNDRV_PCM_RATE_8000_192000,
+                               .formats = TAS2557_FORMATS,
+                       },
+               .ops = &tas2557_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "tas2557 ASIM",
+               .id = 2,
+               .playback = {
+                               .stream_name = "ASIM Playback",
+                               .channels_min = 2,
+                               .channels_max = 2,
+                               .rates = SNDRV_PCM_RATE_8000_192000,
+                               .formats = TAS2557_FORMATS,
+                       },
+               .ops = &tas2557_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+int tas2557_register_codec(struct tas2557_priv *pTAS2557)
+{
+       int nResult = 0;
+
+       dev_info(pTAS2557->dev, "%s, enter\n", __FUNCTION__);
+         
+       nResult = snd_soc_register_codec(pTAS2557->dev, 
+               &soc_codec_driver_tas2557,
+               tas2557_dai_driver, ARRAY_SIZE(tas2557_dai_driver));
+               
+       return nResult;
+}
+
+int tas2557_deregister_codec(struct tas2557_priv *pTAS2557)
+{
+       snd_soc_unregister_codec(pTAS2557->dev);
+               
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 ALSA SOC Smart Amplifier driver");
+MODULE_LICENSE("GPLv2");
+#endif
diff --git a/tas2557-codec.h b/tas2557-codec.h
new file mode 100755 (executable)
index 0000000..aa65bb9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-codec.h
+**
+** Description:
+**     header file for tas2557-codec.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_CODEC_H
+#define _TAS2557_CODEC_H
+
+#include "tas2557.h"
+
+extern int tas2557_register_codec(struct tas2557_priv *pTAS2557);
+extern int tas2557_deregister_codec(struct tas2557_priv *pTAS2557);    
+
+#endif /* _TAS2557_CODEC_H */
diff --git a/tas2557-core.c b/tas2557-core.c
new file mode 100755 (executable)
index 0000000..22cee30
--- /dev/null
@@ -0,0 +1,1215 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-core.c
+**
+** Description:
+**     TAS2557 common functions for Android Linux
+**
+** =============================================================================
+*/
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+
+#include "tas2557.h"
+#include "tas2557-core.h"
+
+#define TAS2557_CAL_NAME    "/data/tas2557_cal.bin"
+
+//set default PLL CLKIN to GPI2 (MCLK) = 0x00
+#define TAS2557_DEFAULT_PLL_CLKIN 0x00
+
+static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
+       char *pFileName);
+static void tas2557_load_data(struct tas2557_priv *pTAS2557, TData * pData,
+       unsigned int nType);
+static void tas2557_load_block(struct tas2557_priv *pTAS2557, TBlock * pBlock);
+static void tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+       unsigned int nConfiguration, bool bLoadSame);
+       
+#define TAS2557_UDELAY 0xFFFFFFFE
+#define TAS2557_MDELAY 0xFFFFFFFD
+
+#define FW_ERR_HEADER -1
+#define FW_ERR_SIZE -2
+
+#define TAS2557_BLOCK_PLL                              0x00
+#define TAS2557_BLOCK_PGM_ALL                  0x0d
+#define TAS2557_BLOCK_PGM_DEV_A                        0x01
+#define TAS2557_BLOCK_PGM_DEV_B                        0x08
+#define TAS2557_BLOCK_CFG_COEFF_DEV_A  0x03
+#define TAS2557_BLOCK_CFG_COEFF_DEV_B  0x0a
+#define TAS2557_BLOCK_CFG_PRE_DEV_A            0x04
+#define TAS2557_BLOCK_CFG_PRE_DEV_B            0x0b
+#define TAS2557_BLOCK_CFG_POST                 0x05
+#define TAS2557_BLOCK_CFG_POST_POWER   0x06
+#define TAS2557_BLOCK_CFG_CAL_A                        0x10
+#define TAS2557_BLOCK_CFG_CAL_B                        0x20
+
+static unsigned int p_tas2557_dr_boost_data[] = {
+       TAS2557_DRBOOST_CTL_REG, 0x08,  //enable, 0x0c=disable
+       TAS2557_DRBOOST_CFG_REG, 0x03,  //threshold -18dB +hysteresis 4dB
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+//This is only needed for PG1.0
+static unsigned int p_tas2557_vpred_comp_data[] =
+{
+//Reverse attenuation compensation for Vpred 0.5dB
+       TAS2557_VPRED_COMP_REG, 0x04, 0x43, 0xca, 0xd0, 0x22,
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+//This is only needed for PG1.0.
+static unsigned int p_tas2557_thermal_foldback_data[] = {
+       TAS2557_THERMAL_FOLDBACK_REG, 0x04, 0x48, 0x00, 0x00, 0x00,     //disable
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_boost_8Ohm_data[] =
+{
+       TAS2557_BOOST_HEADROOM, 0x04, 0x04, 0xcc, 0xcc, 0xcc,   // boost headroom 600mv
+    TAS2557_BOOSTOFF_EFFICIENCY, 0x04, 0x67, 0xae,0x14,0x7a, //boost off efficiency 0.81
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_boost_6Ohm_data[] =
+{
+       TAS2557_BOOST_HEADROOM, 0x04, 0x06, 0x66, 0x66, 0x66, // boost headroom 800mv
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_boost_4Ohm_data[] =
+{
+       TAS2557_BOOST_HEADROOM, 0x04, 0x06, 0x66, 0x66, 0x66, // boost headroom 800mv
+       TAS2557_BOOSTON_EFFICIENCY, 0x04, 0x73, 0x33, 0x33, 0x33,
+       TAS2557_BOOSTOFF_EFFICIENCY, 0x04, 0x60, 0x00,0x00,0x00, //boost off efficiency 0.75
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/* This is only required for PG2.0*/
+static unsigned int p_tas2557_isense_threshold_data[] =
+{
+       TAS2557_ISENSE_THRESHOLD,       0x04, 0, 0, 0, 0,       // Make Isense threshold zero
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+#define TAS2557_STARTUP_DATA_PLL_CLKIN_INDEX 3
+static unsigned int p_tas2557_startup_data[] = {
+       TAS2557_CLK_ERR_CTRL, 0x03,     //enable clock error detection
+       TAS2557_PLL_CLKIN_REG, TAS2557_DEFAULT_PLL_CLKIN,
+       TAS2557_POWER_CTRL2_REG, 0xA0,  //Class-D, Boost power up
+       TAS2557_POWER_CTRL2_REG, 0xA3,  //Class-D, Boost, IV sense power up
+       TAS2557_POWER_CTRL1_REG, 0xF8,  //PLL, DSP, clock dividers power up
+       TAS2557_UDELAY, 2000,           //delay
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_unmute_data[] = {
+       TAS2557_MUTE_REG, 0x00,         //unmute
+       TAS2557_SOFT_MUTE_REG, 0x00,    //soft unmute
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_shutdown_data[] = {
+       TAS2557_SOFT_MUTE_REG, 0x01,    //soft mute
+       TAS2557_UDELAY, 10000,          //delay 10ms
+       TAS2557_MUTE_REG, 0x03,         //mute
+       TAS2557_POWER_CTRL1_REG, 0x60,  //DSP power down
+       TAS2557_UDELAY, 2000,           //delay 2ms
+       TAS2557_POWER_CTRL2_REG, 0x00,  //Class-D, Boost power down
+       TAS2557_POWER_CTRL1_REG, 0x00,  //all power down
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+#if 0
+static unsigned int p_tas2557_shutdown_clk_err[] = {
+       TAS2557_CLK_ERR_CTRL, 0x09,     //enable clock error detection on PLL
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+#endif
+
+static unsigned int p_tas2557_mute_DSP_down_data[] = {
+       TAS2557_MUTE_REG, 0x03,         //mute
+       TAS2557_POWER_CTRL1_REG, 0x60,  //DSP power down
+       TAS2557_UDELAY, 0xFF,           //delay
+       0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static int tas2557_dev_load_data(struct tas2557_priv *pTAS2557,
+       unsigned int *pData)
+{
+       int ret = 0;
+       unsigned int n = 0;
+       unsigned int nRegister;
+       unsigned int nData;
+
+       do {
+               nRegister = pData[n * 2];
+               nData = pData[n * 2 + 1];
+               if (nRegister == TAS2557_UDELAY)
+                       udelay(nData);
+               else if (nRegister != 0xFFFFFFFF){
+                       ret = pTAS2557->write(pTAS2557, nRegister, nData);
+                       if(ret < 0) {
+                               dev_err(pTAS2557->dev, "Reg Write err %d\n", ret);
+                               break;
+                       }
+               }
+               n++;
+       } while (nRegister != 0xFFFFFFFF);
+       
+       return ret;
+}
+
+static int tas2557_dev_load_blk_data(struct tas2557_priv *pTAS2557,
+                                 unsigned int *pData) {
+       unsigned int nRegister;
+       unsigned int *nData;
+       unsigned char Buf[128];
+       unsigned int nLength = 0;
+       unsigned int nLoop = 0;
+       unsigned int i =0;
+       unsigned int nSize = 0;
+       int ret = 0;
+
+       do{
+               nRegister = pData[nLength];
+               nLoop = pData[nLength + 1];
+               nData = &pData[nLength + 2];
+               if (nRegister == TAS2557_MDELAY){
+                       mdelay(nData[0]);
+               }
+               else{
+                       if (nRegister != 0xFFFFFFFF){
+                               if(nSize > 128){ 
+                                       dev_err(pTAS2557->dev, 
+                                               "%s, Line=%d, invalid size, maximum is 128 bytes!\n", 
+                                               __FUNCTION__, __LINE__);
+                                       break;
+                               }
+                               
+                               if(nSize > 1){
+                                       for(i = 0; i < nSize; i++) Buf[i] = (unsigned char)nData[i];
+                                       ret = pTAS2557->bulk_write(pTAS2557, nRegister, Buf, nSize);
+                               }else if(nSize == 1){
+                                       ret = pTAS2557->write(pTAS2557,nRegister, nData[0]);
+                               }else{
+                                       dev_err(pTAS2557->dev, 
+                                               "%s, Line=%d,invalid size, minimum is 1 bytes!\n", 
+                                               __FUNCTION__, __LINE__);
+                               }
+                               
+                               if(ret < 0) break;
+                       }
+               }
+               nLength = nLength + 2 + pData[nLength+1] ;
+       } while (nRegister != 0xFFFFFFFF);
+       
+       return ret;
+}
+
+int tas2557_setLoad(struct tas2557_priv *pTAS2557, int load)
+{
+       int ret = 0;
+
+       ret = pTAS2557->write(pTAS2557, 
+                       TAS2557_SNS_CTRL_REG, (load&0x03)<<1);
+       if(ret<0)
+               goto err;
+       
+       switch(load)
+       {
+               case 0: //8Ohm
+               ret = tas2557_dev_load_blk_data(pTAS2557, 
+                       p_tas2557_boost_8Ohm_data);
+               break;
+               
+               case 1: //6Ohm
+               ret = tas2557_dev_load_blk_data(pTAS2557, 
+                       p_tas2557_boost_6Ohm_data);
+               break;
+               
+               case 2: //4Ohm
+               ret = tas2557_dev_load_blk_data(pTAS2557, 
+                       p_tas2557_boost_4Ohm_data);
+               break;
+       }
+       
+err:   
+       
+       return ret;
+}
+
+int tas2557_load_platdata(struct tas2557_priv *pTAS2557)
+{
+       int ret = 0;
+       
+       ret = tas2557_setLoad(pTAS2557, pTAS2557->mnLoad);
+               
+       return ret;
+}
+
+int tas2557_load_default(struct tas2557_priv *pTAS2557)
+{
+       int ret = 0;
+       
+       ret = tas2557_dev_load_blk_data(pTAS2557, p_tas2557_dr_boost_data);
+       if(ret < 0) goto err;
+       
+       /* This is not required for PG1.0 and 2.1, only PG2.0*/
+       if(pTAS2557->mnPGID 
+               == TAS2557_PG_VERSION_2P0)
+               ret = tas2557_dev_load_blk_data(pTAS2557, 
+                               p_tas2557_isense_threshold_data);
+                               
+       if(pTAS2557->mnPGID 
+               == TAS2557_PG_VERSION_1P0){
+               tas2557_dev_load_blk_data(pTAS2557, p_tas2557_vpred_comp_data);
+               tas2557_dev_load_blk_data(pTAS2557, p_tas2557_thermal_foldback_data);
+       }
+       
+       tas2557_load_platdata(pTAS2557);
+       
+err:
+       
+       return ret;
+}
+
+void tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable)
+{
+       dev_dbg(pTAS2557->dev, "Enable: %d\n", bEnable);
+       if (bEnable) {
+               if (!pTAS2557->mbPowerUp) {
+                       dev_dbg(pTAS2557->dev, "Enable: load startup sequence\n");
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+                       dev_dbg(pTAS2557->dev, "Enable: load unmute sequence\n");
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+                       pTAS2557->mbPowerUp = true;
+               }
+       } else {
+               if (pTAS2557->mbPowerUp) {
+                       dev_dbg(pTAS2557->dev, "Enable: load shutdown sequence\n");
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+                       //tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_clk_err);
+                       pTAS2557->mbPowerUp = false;
+               }
+       }
+}
+
+int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSamplingRate)
+{
+       TConfiguration *pConfiguration;
+       unsigned int nConfiguration;
+
+       dev_dbg(pTAS2557->dev, "tas2557_setup_clocks: nSamplingRate = %d [Hz]\n",
+               nSamplingRate);
+
+       if ((!pTAS2557->mpFirmware->mpPrograms) ||
+               (!pTAS2557->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2557->dev, "Firmware not loaded\n");
+               return -EINVAL;
+       }
+
+       pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+       if (pConfiguration->mnSamplingRate == nSamplingRate) {
+               dev_info(pTAS2557->dev, "Sampling rate for current configuration matches: %d\n",
+                       nSamplingRate);
+               return 0;
+       }
+
+       for (nConfiguration = 0;
+               nConfiguration < pTAS2557->mpFirmware->mnConfigurations;
+               nConfiguration++) {
+               pConfiguration =
+                       &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+               if ((pConfiguration->mnSamplingRate == nSamplingRate)
+                       &&(pConfiguration->mnProgram == pTAS2557->mnCurrentProgram)){
+                       dev_info(pTAS2557->dev,
+                               "Found configuration: %s, with compatible sampling rate %d\n",
+                               pConfiguration->mpName, nSamplingRate);
+                       tas2557_load_configuration(pTAS2557, nConfiguration, false);
+                       return 0;
+               }
+       }
+
+       dev_err(pTAS2557->dev, "Cannot find a configuration that supports sampling rate: %d\n",
+               nSamplingRate);
+
+       return -EINVAL;
+}
+
+static void fw_print_header(struct tas2557_priv *pTAS2557, TFirmware * pFirmware)
+{
+       dev_info(pTAS2557->dev, "FW Size        = %d", pFirmware->mnFWSize);
+       dev_info(pTAS2557->dev, "Checksum       = 0x%04X", pFirmware->mnChecksum);
+       dev_info(pTAS2557->dev, "PPC Version    = 0x%04X", pFirmware->mnPPCVersion);
+       dev_info(pTAS2557->dev, "FW  Version    = 0x%04X", pFirmware->mnFWVersion);
+       dev_info(pTAS2557->dev, "Driver Version = 0x%04X", pFirmware->mnDriverVersion);
+       dev_info(pTAS2557->dev, "Timestamp      = %d", pFirmware->mnTimeStamp);
+       dev_info(pTAS2557->dev, "DDC Name       = %s", pFirmware->mpDDCName);
+       dev_info(pTAS2557->dev, "Description    = %s", pFirmware->mpDescription);
+}
+
+inline unsigned int fw_convert_number(unsigned char *pData)
+{
+       return pData[3] + (pData[2] << 8) + (pData[1] << 16) + (pData[0] << 24);
+}
+
+static int fw_parse_header(struct tas2557_priv *pTAS2557, 
+       TFirmware * pFirmware, unsigned char *pData,
+       unsigned int nSize)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 };
+       if (nSize < 102) {
+               dev_err(pTAS2557->dev, "Firmware: Header too short");
+               return -1;
+       }
+
+       if (memcmp(pData, pMagicNumber, 4)) {
+               dev_err(pTAS2557->dev, "Firmware: Magic number doesn't match");
+               return -1;
+       }
+
+       pData += 4;
+
+       pFirmware->mnFWSize = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnChecksum = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnPPCVersion = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnFWVersion = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnDriverVersion = fw_convert_number(pData);
+       pData += 4;             
+                       
+       pFirmware->mnTimeStamp = fw_convert_number(pData);
+       pData += 4;
+
+       memcpy(pFirmware->mpDDCName, pData, 64);
+       pData += 64;
+
+       n = strlen(pData);
+       pFirmware->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+       pData += n + 1;
+
+       if ((pData - pDataStart) >= nSize) {
+               dev_err(pTAS2557->dev, "Firmware: Header too short after DDC description");
+               return -1;
+       }
+
+       pFirmware->mnDeviceFamily = fw_convert_number(pData);
+       pData += 4;
+
+       if(pFirmware->mnDeviceFamily != 0){
+               dev_err(pTAS2557->dev, 
+                       "deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily);
+               return -1;
+       }
+       
+       pFirmware->mnDevice = fw_convert_number(pData);
+       pData += 4;
+
+       if(pFirmware->mnDevice != 2){
+               dev_err(pTAS2557->dev, 
+                       "device %d, not TAS2557", pFirmware->mnDevice);
+               return -1;
+       }
+       
+       fw_print_header(pTAS2557, pFirmware);
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_block_data(TBlock * pBlock, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+
+       pBlock->mnType = fw_convert_number(pData);
+       pData += 4;
+
+       pBlock->mnCommands = fw_convert_number(pData);
+       pData += 4;
+
+       n = pBlock->mnCommands * 4;
+       pBlock->mpData = kmemdup(pData, n, GFP_KERNEL);
+       pData += n;
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_data(TData * pImageData, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int nBlock;
+       unsigned int n;
+
+       memcpy(pImageData->mpName, pData, 64);
+       pData += 64;
+
+       n = strlen(pData);
+       pImageData->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+       pData += n + 1;
+
+       pImageData->mnBlocks = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pImageData->mpBlocks =
+               kmalloc(sizeof(TBlock) * pImageData->mnBlocks, GFP_KERNEL);
+
+       for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+               n = fw_parse_block_data(&(pImageData->mpBlocks[nBlock]), pData);
+               pData += n;
+       }
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_pll_data(TFirmware * pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nPLL;
+       TPLL *pPLL;
+
+       pFirmware->mnPLLs = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pFirmware->mpPLLs = kmalloc(sizeof(TPLL) * pFirmware->mnPLLs, GFP_KERNEL);
+       for (nPLL = 0; nPLL < pFirmware->mnPLLs; nPLL++) {
+               pPLL = &(pFirmware->mpPLLs[nPLL]);
+
+               memcpy(pPLL->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pPLL->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               n = fw_parse_block_data(&(pPLL->mBlock), pData);
+               pData += n;
+       }
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_program_data(TFirmware * pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nProgram;
+       TProgram *pProgram;
+
+       pFirmware->mnPrograms = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pFirmware->mpPrograms =
+               kmalloc(sizeof(TProgram) * pFirmware->mnPrograms, GFP_KERNEL);
+       for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+               pProgram = &(pFirmware->mpPrograms[nProgram]);
+               memcpy(pProgram->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pProgram->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               pProgram->mnAppMode = pData[0];
+               pData++;
+               
+               pProgram->mnBoost = (pData[0] << 8) + pData[1];
+               pData += 2;
+               
+               n = fw_parse_data(&(pProgram->mData), pData);
+               pData += n;
+       }
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_configuration_data(TFirmware * pFirmware,
+       unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nConfiguration;
+       TConfiguration *pConfiguration;
+
+       pFirmware->mnConfigurations = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pFirmware->mpConfigurations =
+               kmalloc(sizeof(TConfiguration) * pFirmware->mnConfigurations,
+               GFP_KERNEL);
+       for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
+               nConfiguration++) {
+               pConfiguration = &(pFirmware->mpConfigurations[nConfiguration]);
+               memcpy(pConfiguration->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pConfiguration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               pConfiguration->mnProgram = pData[0];
+               pData++;
+
+               pConfiguration->mnPLL = pData[0];
+               pData++;
+
+               pConfiguration->mnSamplingRate = fw_convert_number(pData);
+               pData += 4;
+
+               n = fw_parse_data(&(pConfiguration->mData), pData);
+               pData += n;
+       }
+
+       return pData - pDataStart;
+}
+
+int fw_parse_calibration_data(TFirmware * pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nCalibration;
+       TCalibration *pCalibration;
+
+       pFirmware->mnCalibrations = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pFirmware->mpCalibrations =
+               kmalloc(sizeof(TCalibration) * pFirmware->mnCalibrations, GFP_KERNEL);
+       for (nCalibration = 0;
+               nCalibration < pFirmware->mnCalibrations;
+               nCalibration++) {
+               pCalibration = &(pFirmware->mpCalibrations[nCalibration]);
+               memcpy(pCalibration->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pCalibration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               pCalibration->mnProgram = pData[0];
+               pData++;
+
+               pCalibration->mnConfiguration = pData[0];
+               pData++;
+
+               n = fw_parse_block_data(&(pCalibration->mBlock), pData);
+               pData += n;
+       }
+
+       return pData - pDataStart;
+}
+
+static int fw_parse(struct tas2557_priv *pTAS2557,
+       TFirmware * pFirmware,
+       unsigned char *pData,
+       unsigned int nSize)
+{
+       int nPosition = 0;
+
+       nPosition = fw_parse_header(pTAS2557, pFirmware, pData, nSize);
+       if (nPosition < 0) {
+               dev_err(pTAS2557->dev, "Firmware: Wrong Header");
+               return FW_ERR_HEADER;
+       }
+
+       if (nPosition >= nSize) {
+               dev_err(pTAS2557->dev, "Firmware: Too short");
+               return FW_ERR_SIZE;
+       }
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       nPosition = fw_parse_pll_data(pFirmware, pData);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       nPosition = fw_parse_program_data(pFirmware, pData);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       nPosition = fw_parse_configuration_data(pFirmware, pData);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       if (nSize > 64)
+               nPosition = fw_parse_calibration_data(pFirmware, pData);
+
+       return 0;
+}
+
+static void tas2557_load_block(struct tas2557_priv *pTAS2557, TBlock * pBlock)
+{
+       unsigned int nCommand = 0;
+       unsigned char nBook;
+       unsigned char nPage;
+       unsigned char nOffset;
+       unsigned char nData;
+       unsigned int nLength;
+       unsigned char *pData = pBlock->mpData;
+
+       dev_dbg(pTAS2557->dev, "TAS2557 load block: Type = %d, commands = %d\n",
+               pBlock->mnType, pBlock->mnCommands);
+       while (nCommand < pBlock->mnCommands) {
+               pData = pBlock->mpData + nCommand * 4;
+
+               nBook = pData[0];
+               nPage = pData[1];
+               nOffset = pData[2];
+               nData = pData[3];
+
+               nCommand++;
+
+               if (nOffset <= 0x7F){
+                       pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset),
+                               nData);
+               }else if (nOffset == 0x81) {
+                       unsigned int nSleep = (nBook << 8) + nPage;
+                       msleep(nSleep);
+               }else if (nOffset == 0x85) {
+                       pData += 4;
+                       nLength = (nBook << 8) + nPage;
+                       nBook = pData[0];
+                       nPage = pData[1];
+                       nOffset = pData[2];
+                       if (nLength > 1)
+                               pTAS2557->bulk_write(pTAS2557, TAS2557_REG(nBook, nPage,
+                                               nOffset), pData + 3, nLength);
+                       else
+                               pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset),
+                                       pData[3]);
+
+                       nCommand++;
+                       if (nLength >= 2)
+                               nCommand += ((nLength - 2) / 4) + 1;
+               }
+       }
+}
+
+static void tas2557_load_data(struct tas2557_priv *pTAS2557, TData * pData,
+       unsigned int nType)
+{
+       unsigned int nBlock;
+       TBlock *pBlock;
+
+       dev_dbg(pTAS2557->dev,
+               "TAS2557 load data: %s, Blocks = %d, Block Type = %d\n", pData->mpName,
+               pData->mnBlocks, nType);
+
+       for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) {
+               pBlock = &(pData->mpBlocks[nBlock]);
+               if (pBlock->mnType == nType)
+                       tas2557_load_block(pTAS2557, pBlock);
+       }
+}
+
+static void tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+       unsigned int nConfiguration, bool bLoadSame)
+{
+       TConfiguration *pCurrentConfiguration;
+       TConfiguration *pNewConfiguration;
+       TPLL *pNewPLL;
+
+       dev_dbg(pTAS2557->dev, "tas2557_load_configuration: %d\n", nConfiguration);
+
+       if ((!pTAS2557->mpFirmware->mpPrograms) ||
+               (!pTAS2557->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2557->dev, "Firmware not loaded\n");
+               return;
+       }
+
+       if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
+                       nConfiguration);
+               return;
+       }
+
+       if ((nConfiguration == pTAS2557->mnCurrentConfiguration) && (!bLoadSame)) {
+               dev_info(pTAS2557->dev, "Configuration %d is already loaded\n",
+                       nConfiguration);
+               return;
+       }
+
+       pCurrentConfiguration =
+               &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+       pNewConfiguration =
+               &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+
+       if (pNewConfiguration->mnProgram != pCurrentConfiguration->mnProgram) {
+               dev_err(pTAS2557->dev,
+                       "Configuration %d, %s doesn't share the same program as current %d\n",
+                       nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram);
+               return;
+       }
+
+       if (pNewConfiguration->mnPLL >= pTAS2557->mpFirmware->mnPLLs) {
+               dev_err(pTAS2557->dev,
+                       "Configuration %d, %s doesn't have a valid PLL index %d\n",
+                       nConfiguration, pNewConfiguration->mpName, pNewConfiguration->mnPLL);
+               return;
+       }
+       
+       pNewPLL = &(pTAS2557->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]);
+
+       if (pTAS2557->mbPowerUp) {
+               if (pNewConfiguration->mnPLL != pCurrentConfiguration->mnPLL) {
+                       dev_dbg(pTAS2557->dev,
+                               "TAS2557 is powered up -> mute and power down DSP before loading new configuration\n");
+                       //tas2557_dev_load_data(pTAS2557, p_tas2557_mute_DSP_down_data);
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+                       dev_dbg(pTAS2557->dev, "TAS2557: load new PLL: %s, block data\n",
+                               pNewPLL->mpName);
+                       tas2557_load_block(pTAS2557, &(pNewPLL->mBlock));
+                       pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
+                       dev_dbg(pTAS2557->dev,
+                               "load new configuration: %s, pre block data\n",
+                               pNewConfiguration->mpName);
+                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+                               TAS2557_BLOCK_CFG_PRE_DEV_A);
+                       dev_dbg(pTAS2557->dev,
+                               "TAS2557: load new configuration: %s, coeff block data\n",
+                               pNewConfiguration->mpName);
+                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+                               TAS2557_BLOCK_CFG_COEFF_DEV_A);
+                       dev_dbg(pTAS2557->dev, "TAS2557: power up TAS2557\n");
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+                       dev_dbg(pTAS2557->dev, "TAS2557: unmute TAS2557\n");
+                       tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+               } else {
+                       dev_dbg(pTAS2557->dev,
+                               "TAS2557 is powered up, no change in PLL: load new configuration: %s, coeff block data\n",
+                               pNewConfiguration->mpName);
+                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+                               TAS2557_BLOCK_CFG_COEFF_DEV_A); 
+               }
+                               
+               pTAS2557->mbLoadConfigurationPostPowerUp = false;
+       } else {
+               dev_dbg(pTAS2557->dev,
+                       "TAS2557 was powered down -> set flag to load configuration data when OS powers up the TAS2557 the next time\n");
+               if (pNewConfiguration->mnPLL != pCurrentConfiguration->mnPLL) {
+                       dev_dbg(pTAS2557->dev, "TAS2557: load new PLL: %s, block data\n",
+                               pNewPLL->mpName);
+                       tas2557_load_block(pTAS2557, &(pNewPLL->mBlock));
+                       pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
+                       dev_dbg(pTAS2557->dev,
+                               "load new configuration: %s, pre block data\n",
+                               pNewConfiguration->mpName);
+                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+                               TAS2557_BLOCK_CFG_PRE_DEV_A);
+               }
+               
+               dev_dbg(pTAS2557->dev,
+                               "TAS2557: load new configuration: %s, coeff block data\n",
+                               pNewConfiguration->mpName);
+               tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+                               TAS2557_BLOCK_CFG_COEFF_DEV_A); 
+               
+               pTAS2557->mbLoadConfigurationPostPowerUp = true;
+       }
+
+       pTAS2557->mnCurrentConfiguration = nConfiguration;
+}
+
+int tas2557_set_config(struct tas2557_priv *pTAS2557, int config)
+{
+       TConfiguration *pConfiguration;
+       TProgram *pProgram;
+       unsigned int nProgram = pTAS2557->mnCurrentProgram;
+       unsigned int nConfiguration = config;
+
+       if ((!pTAS2557->mpFirmware->mpPrograms) ||
+               (!pTAS2557->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2557->dev, "Firmware not loaded\n");
+               return -1;
+       }
+
+       if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
+                       nConfiguration);
+               return -1;
+       }
+
+       pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+       pProgram = &(pTAS2557->mpFirmware->mpPrograms[nProgram]);
+
+       if (nProgram != pConfiguration->mnProgram) {
+               dev_err(pTAS2557->dev,
+                       "Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n",
+                       nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram,
+                       nProgram, pProgram->mpName);
+               return -1;
+       }
+
+       tas2557_load_configuration(pTAS2557, nConfiguration, false);
+
+       return 0;
+}
+
+void tas2557_clear_firmware(TFirmware *pFirmware)
+{
+       unsigned int n, nn;
+       if (!pFirmware) return;
+       if (pFirmware->mpDescription) kfree(pFirmware->mpDescription);  
+
+       for (n = 0; n < pFirmware->mnPLLs; n++)
+       {
+               kfree(pFirmware->mpPLLs[n].mpDescription);
+               kfree(pFirmware->mpPLLs[n].mBlock.mpData);
+       }
+       kfree(pFirmware->mpPLLs);
+
+       for (n = 0; n < pFirmware->mnPrograms; n++)
+       {
+               kfree(pFirmware->mpPrograms[n].mpDescription);
+               kfree(pFirmware->mpPrograms[n].mData.mpDescription);
+               for (nn = 0; nn < pFirmware->mpPrograms[n].mData.mnBlocks; nn++)
+                       kfree(pFirmware->mpPrograms[n].mData.mpBlocks[nn].mpData);
+               kfree(pFirmware->mpPrograms[n].mData.mpBlocks);
+       }
+       kfree(pFirmware->mpPrograms);
+
+       for (n = 0; n < pFirmware->mnConfigurations; n++)
+       {
+               kfree(pFirmware->mpConfigurations[n].mpDescription);
+               kfree(pFirmware->mpConfigurations[n].mData.mpDescription);
+               for (nn = 0; nn < pFirmware->mpConfigurations[n].mData.mnBlocks; nn++)
+                       kfree(pFirmware->mpConfigurations[n].mData.mpBlocks[nn].mpData);
+               kfree(pFirmware->mpConfigurations[n].mData.mpBlocks);
+       }
+       kfree(pFirmware->mpConfigurations);
+
+       for (n = 0; n < pFirmware->mnCalibrations; n++)
+       {
+               kfree(pFirmware->mpCalibrations[n].mpDescription);
+               kfree(pFirmware->mpCalibrations[n].mBlock.mpData);
+       }
+       kfree(pFirmware->mpCalibrations);
+
+       memset(pFirmware, 0x00, sizeof(TFirmware));
+}
+
+static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
+       char *pFileName)
+{
+       int nResult;
+       int nFile;
+       mm_segment_t fs;
+       unsigned char pBuffer[512];
+       int nSize = 0;
+
+       dev_dbg(pTAS2557->dev, "%s:\n", __func__);
+
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+       nFile = sys_open(pFileName, O_RDONLY, 0);
+
+       dev_info(pTAS2557->dev, "TAS2557 calibration file = %s, handle = %d\n",
+               pFileName, nFile);
+
+       if (nFile >= 0) {
+               nSize = sys_read(nFile, pBuffer, 512);
+               sys_close(nFile);
+       } else {
+               dev_err(pTAS2557->dev, "TAS2557 cannot open calibration file: %s\n",
+                       pFileName);
+       }
+
+       set_fs(fs);
+
+       if (!nSize)
+               return;
+
+       tas2557_clear_firmware(pTAS2557->mpCalFirmware);
+               
+       dev_info(pTAS2557->dev, "TAS2557 calibration file size = %d\n", nSize);
+       nResult = fw_parse(pTAS2557, pTAS2557->mpCalFirmware, pBuffer, nSize);
+
+       if (nResult) {
+               dev_err(pTAS2557->dev, "TAS2557 calibration file is corrupt\n");
+               return;
+       }
+
+       dev_info(pTAS2557->dev, "TAS2557 calibration: %d calibrations\n",
+               pTAS2557->mpCalFirmware->mnCalibrations);
+}
+
+void tas2557_fw_ready(const struct firmware *pFW, void *pContext)
+{
+       struct tas2557_priv *pTAS2557 = (struct tas2557_priv *) pContext;
+       int nResult;
+       unsigned int nProgram = 0;
+       unsigned int nSampleRate = 0;
+
+       dev_info(pTAS2557->dev, "%s:\n", __func__);
+
+       if (unlikely(!pFW) || unlikely(!pFW->data)) {
+               dev_err(pTAS2557->dev, "%s firmware is not loaded.\n",
+                       TAS2557_FW_NAME);
+               
+               nResult = tas2557_load_default(pTAS2557);
+               return;
+       }
+
+       if (pTAS2557->mpFirmware->mpConfigurations){
+               nProgram = pTAS2557->mnCurrentProgram;
+               nSampleRate = pTAS2557->mnCurrentSampleRate;
+               dev_dbg(pTAS2557->dev, "clear current firmware\n");
+               tas2557_clear_firmware(pTAS2557->mpFirmware);
+       }       
+               
+       nResult = fw_parse(pTAS2557, pTAS2557->mpFirmware, 
+               (unsigned char *) (pFW->data),  pFW->size);
+
+       release_firmware(pFW);
+       
+       if (nResult) {
+               dev_err(pTAS2557->dev, "firmware is corrupt\n");
+               return;
+       }
+
+       if (!pTAS2557->mpFirmware->mnPrograms) {
+               dev_err(pTAS2557->dev, "firmware contains no programs\n");
+               return;
+       }
+       
+       if (!pTAS2557->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2557->dev, 
+                       "firmware contains no configurations\n");
+               return;
+       }
+       
+       if(nProgram >= pTAS2557->mpFirmware->mnPrograms){
+               dev_info(pTAS2557->dev, 
+                       "no previous program, set to default\n");
+               nProgram = 0;
+       }
+               
+       pTAS2557->mnCurrentSampleRate = nSampleRate;
+
+       tas2557_set_program(pTAS2557, nProgram);
+}      
+
+int tas2557_set_program(struct tas2557_priv *pTAS2557,
+       unsigned int nProgram)
+{
+       TPLL *pPLL;
+       TConfiguration *pConfiguration;
+       unsigned int nConfiguration = 0;
+       unsigned int nSampleRate = 0;
+       unsigned int Value = 0;
+       bool bFound = false;
+       int nResult = -1;
+
+       if ((!pTAS2557->mpFirmware->mpPrograms) ||
+               (!pTAS2557->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2557->dev, "Firmware not loaded\n");
+               return -1;
+       }
+       
+       if (nProgram >= pTAS2557->mpFirmware->mnPrograms) {
+               dev_err(pTAS2557->dev, "TAS2557: Program %d doesn't exist\n",
+                       nConfiguration);
+               return -1;
+       }
+       
+       nConfiguration = 0;
+       nSampleRate = pTAS2557->mnCurrentSampleRate;
+       
+       while (!bFound 
+               && (nConfiguration < pTAS2557->mpFirmware->mnConfigurations)) {
+               if (pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnProgram 
+                       == nProgram){
+                       if(nSampleRate == 0){
+                               bFound = true;
+                               dev_info(pTAS2557->dev, "find default configuration %d\n", nConfiguration);
+                       }else if(nSampleRate 
+                               == pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate){
+                               bFound = true;
+                               dev_info(pTAS2557->dev, "find matching configuration %d\n", nConfiguration);
+                       }else{
+                               nConfiguration++;
+                       }
+               }else{
+                       nConfiguration++;
+               }
+       }
+       
+       if (!bFound) {
+               dev_err(pTAS2557->dev, 
+                       "Program %d, no valid configuration found for sample rate %d, ignore\n",
+                       nProgram, nSampleRate);
+               return -1;
+       }
+       
+       pTAS2557->mnCurrentProgram = nProgram;
+       
+       nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_mute_DSP_down_data);
+       if(nResult < 0){
+               dev_err(pTAS2557->dev, 
+                       "device register access error\n");
+               return -1;
+       } 
+               
+       pTAS2557->write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+
+       udelay(1000);
+       pTAS2557->mnCurrentBook = 0;
+       pTAS2557->mnCurrentPage = 0;
+       
+       nResult = tas2557_load_default(pTAS2557);       
+       
+       dev_info(pTAS2557->dev, "load program %d\n", nProgram);
+       tas2557_load_data(pTAS2557,
+               &(pTAS2557->mpFirmware->mpPrograms[nProgram].mData),
+               TAS2557_BLOCK_PGM_DEV_A);
+
+       nResult = pTAS2557->read(pTAS2557, TAS2557_CRC_CHECKSUM_REG, &Value);
+       dev_info(pTAS2557->dev, "uCDSP Checksum: 0x%02x\n", Value);
+       
+       pTAS2557->mnCurrentConfiguration = nConfiguration;
+       pConfiguration =
+               &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+       pPLL = &(pTAS2557->mpFirmware->mpPLLs[pConfiguration->mnPLL]);
+       dev_dbg(pTAS2557->dev,
+               "TAS2557 load PLL: %s block for Configuration %s\n",
+               pPLL->mpName, pConfiguration->mpName);
+       
+       tas2557_load_block(pTAS2557, &(pPLL->mBlock));
+       pTAS2557->mnCurrentSampleRate = pConfiguration->mnSamplingRate;
+       dev_dbg(pTAS2557->dev,
+               "load configuration %s conefficient pre block\n",
+               pConfiguration->mpName);                
+       tas2557_load_data(pTAS2557, &(pConfiguration->mData), TAS2557_BLOCK_CFG_PRE_DEV_A);
+
+       nResult = pTAS2557->read(pTAS2557, TAS2557_PLL_CLKIN_REG, &Value);
+       dev_info(pTAS2557->dev, "TAS2557 PLL_CLKIN = 0x%02X\n", Value);
+       p_tas2557_startup_data[TAS2557_STARTUP_DATA_PLL_CLKIN_INDEX] = Value;
+
+       tas2557_load_configuration(pTAS2557, nConfiguration, true);
+       if (pTAS2557->mbPowerUp){
+               dev_dbg(pTAS2557->dev, "device powered up, load startup\n");
+               tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+               dev_dbg(pTAS2557->dev,
+                       "device powered up, load unmute\n");
+               tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+       }
+
+       return 0;
+}
+
+int tas2557_set_calibration(struct tas2557_priv *pTAS2557,
+       int nCalibration)
+{
+       if ((!pTAS2557->mpFirmware->mpPrograms) || (!pTAS2557->mpFirmware->mpConfigurations)) 
+       {
+               dev_err(pTAS2557->dev, "Firmware not loaded\n\r");
+               return -1;
+       }
+
+       if (nCalibration == 0x00FF)
+       {
+               dev_info(pTAS2557->dev, "load new calibration file %s\n", TAS2557_CAL_NAME);    
+               tas2557_load_calibration(pTAS2557, TAS2557_CAL_NAME);
+               nCalibration = 0;
+       }
+
+       if (nCalibration >= pTAS2557->mpFirmware->mnCalibrations) {
+               dev_err(pTAS2557->dev,
+                       "Calibration %d doesn't exist\n", nCalibration);
+               return -1;
+       }
+
+       pTAS2557->mnCurrentCalibration = nCalibration;
+       if(pTAS2557->mbPowerUp){
+               tas2557_load_block(pTAS2557, 
+                       &(pTAS2557->mpCalFirmware->mpCalibrations[pTAS2557->mnCurrentCalibration].mBlock));
+               pTAS2557->mbLoadCalibrationPostPowerUp = false; 
+       }else{
+               pTAS2557->mbLoadCalibrationPostPowerUp = true; 
+       }
+
+       return 0;
+}
+
+int tas2557_parse_dt(struct device *dev,
+                       struct tas2557_priv *pTAS2557)
+{
+       struct device_node *np = dev->of_node;
+       int rc= 0, ret = 0;
+
+       rc = of_property_read_u32(np, "ti,load", &pTAS2557->mnLoad);
+       if (rc) {
+               dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,load", np->full_name, rc);
+               ret = -1;
+       }else{
+               dev_dbg(pTAS2557->dev, "ti,load=%d", pTAS2557->mnLoad);
+       }
+
+       pTAS2557->mnResetGPIO = of_get_named_gpio(np,
+                               "ti,cdc-reset-gpio", 0);
+       if (pTAS2557->mnResetGPIO < 0) {
+               dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,cdc-reset-gpio", np->full_name,
+                       pTAS2557->mnResetGPIO);
+               ret = -1;
+       }else{
+               dev_dbg(pTAS2557->dev, "ti,cdc-reset-gpio=%d", pTAS2557->mnResetGPIO);
+       }
+
+       return ret;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 common functions for Android Linux");
+MODULE_LICENSE("GPLv2");
\ No newline at end of file
diff --git a/tas2557-core.h b/tas2557-core.h
new file mode 100755 (executable)
index 0000000..ea24258
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-core.h
+**
+** Description:
+**     header file for tas2557-core.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_CORE_H
+#define _TAS2557_CORE_H
+
+#include "tas2557.h"
+
+extern int tas2557_parse_dt(struct device *dev,
+                       struct tas2557_priv *pTAS2557);
+extern void tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable);
+extern int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, 
+       unsigned int nSamplingRate);
+extern int tas2557_set_config(struct tas2557_priv *pTAS2557, int config);
+extern void tas2557_load_fs_firmware(struct tas2557_priv *pTAS2557,
+       char *pFileName);
+extern void tas2557_fw_ready(const struct firmware *pFW, void *pContext);
+extern int tas2557_set_program(struct tas2557_priv *pTAS2557,
+       unsigned int nProgram);
+extern int tas2557_set_calibration(struct tas2557_priv *pTAS2557,
+       int nCalibration);
+extern int tas2557_load_default(struct tas2557_priv *pTAS2557);
+       
+#endif /* _TAS2557_CORE_H */
diff --git a/tas2557-misc.c b/tas2557-misc.c
new file mode 100755 (executable)
index 0000000..024ebae
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-misc.c
+**
+** Description:
+**     misc driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_MISC
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+//#include <dt-bindings/sound/tas2557.h>
+
+#include "tas2557.h"
+#include "tas2557-core.h"
+#include "tas2557-misc.h"
+#include <linux/dma-mapping.h>
+
+#define TAS2557_FW_FULL_NAME     "/etc/firmware/tas2557_uCDSP.bin"
+     
+static int g_logEnable = 1;
+static struct tas2557_priv *g_tas2557 = NULL;
+
+static int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, unsigned int nBitRate)
+{
+       int ret = 0, n = -1;
+       
+       dev_dbg(pTAS2557->dev, "tas2557_set_bit_rate: nBitRate = %d \n",
+               nBitRate);
+       switch(nBitRate){
+               case 16:
+                       n = 0;
+               break;
+               case 20:
+                       n = 1;
+               break;
+               case 24:
+                       n = 2;
+               break;
+               case 32:
+                       n = 3;
+               break;
+       }
+       
+       if(n >= 0)
+               ret = pTAS2557->update_bits(pTAS2557, 
+                       TAS2557_ASI1_DAC_FORMAT_REG, 0x18, n<<3);       
+               
+       return ret;     
+}
+                                 
+static int tas2557_file_open(struct inode *inode, struct file *file)
+{
+       struct tas2557_priv *pTAS2557 = g_tas2557;
+       
+       if (!try_module_get(THIS_MODULE)) return -ENODEV;
+
+       file->private_data = (void*)pTAS2557;
+       
+       if(g_logEnable) dev_info(pTAS2557->dev,
+                               "%s\n", __FUNCTION__);
+       return 0;
+}
+
+static int tas2557_file_release(struct inode *inode, struct file *file)
+{
+       struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+
+       if(g_logEnable) dev_info(pTAS2557->dev,
+                               "%s\n", __FUNCTION__);
+                               
+       file->private_data = (void*)NULL;
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+static ssize_t tas2557_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+       int ret = 0;
+       unsigned int nValue = 0;
+       unsigned char value = 0;
+       unsigned char *p_kBuf = NULL;
+
+       mutex_lock(&pTAS2557->file_lock);
+       
+       switch(pTAS2557->mnDBGCmd)
+       {
+               case TIAUDIO_CMD_REG_READ:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                               "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n", pTAS2557->mnCurrentReg, (int)count);
+                       if(count == 1){
+                               ret = pTAS2557->read(pTAS2557, pTAS2557->mnCurrentReg, &nValue);
+                               if( 0 > ret) {
+                                       dev_err(pTAS2557->dev, "dev read fail %d\n", ret);
+                                       break;
+                               }                       
+                               
+                               value = (u8)nValue;
+                               if(g_logEnable) dev_info(pTAS2557->dev,
+                                       "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", 
+                                       nValue, value);
+                               ret = copy_to_user(buf, &value, 1);
+                               if (0 != ret) {
+                                       /* Failed to copy all the data, exit */
+                                       dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                               }       
+                       }else if(count > 1){
+                               p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+                               if(p_kBuf != NULL){
+                                       ret = pTAS2557->bulk_read(pTAS2557, pTAS2557->mnCurrentReg, p_kBuf, count);
+                                       if( 0 > ret) {
+                                               dev_err(pTAS2557->dev, "dev bulk read fail %d\n", ret);
+                                       }else{                                          
+                                               ret = copy_to_user(buf, p_kBuf, count);
+                                               if (0 != ret) {
+                                                       /* Failed to copy all the data, exit */
+                                                       dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                               }
+                                       }
+                                       
+                                       kfree(p_kBuf);
+                               }else{
+                                       dev_err(pTAS2557->dev, "read no mem\n");
+                               }
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_PROGRAM:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_PROGRAM: count = %d\n", 
+                                               (int)count);
+
+                       if(count == PROGRAM_BUF_SIZE){
+                               p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+                               if(p_kBuf != NULL){
+                                       TProgram * pProgram = 
+                                               &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+                                       
+                                       p_kBuf[0] = pTAS2557->mpFirmware->mnPrograms;                                                   
+                                       p_kBuf[1] = pTAS2557->mnCurrentProgram;         
+                                       p_kBuf[2] = pProgram->mnAppMode;        
+                                       p_kBuf[3] = (pProgram->mnBoost&0xff00)>>8;      
+                                       p_kBuf[4] = (pProgram->mnBoost&0x00ff); 
+                                       memcpy(&p_kBuf[5], pProgram->mpName, FW_NAME_SIZE);
+                                       strcpy(&p_kBuf[5+FW_NAME_SIZE], pProgram->mpDescription);
+                                       
+                                       ret = copy_to_user(buf, p_kBuf, count);
+                                       if (0 != ret) {
+                                               /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                                       
+                                       kfree(p_kBuf);
+                               }else{
+                                       dev_err(pTAS2557->dev, "read no mem\n");
+                               }                               
+                       }else{
+                               dev_err(pTAS2557->dev, "read buffer not sufficient\n");
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_CONFIGURATION:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_CONFIGURATION: count = %d\n", 
+                                               (int)count);
+
+                       if(count == CONFIGURATION_BUF_SIZE){
+                               p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+                               if(p_kBuf != NULL){
+                                       TConfiguration * pConfiguration = 
+                                               &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+
+                                       p_kBuf[0] = pTAS2557->mpFirmware->mnConfigurations;                                                                                     
+                                       p_kBuf[1] = pTAS2557->mnCurrentConfiguration;                                   
+                                       memcpy(&p_kBuf[2], pConfiguration->mpName, FW_NAME_SIZE);
+                                       p_kBuf[2+FW_NAME_SIZE] = pConfiguration->mnProgram;
+                                       p_kBuf[3+FW_NAME_SIZE] = pConfiguration->mnPLL;
+                                       p_kBuf[4+FW_NAME_SIZE] = (pConfiguration->mnSamplingRate&0x000000ff);
+                                       p_kBuf[5+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8);
+                                       p_kBuf[6+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16);
+                                       p_kBuf[7+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0xff000000)>>24);
+                                       strcpy(&p_kBuf[8+FW_NAME_SIZE], pConfiguration->mpDescription);
+                                       
+                                       ret = copy_to_user(buf, p_kBuf, count);
+                                       if (0 != ret) {
+                                               /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                                       
+                                       kfree(p_kBuf);
+                               }else{
+                                       dev_err(pTAS2557->dev, "read no mem\n");
+                               }                               
+                       }else{
+                               dev_err(pTAS2557->dev, "read buffer not sufficient\n");
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_FW_TIMESTAMP:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_FW_TIMESTAMP: count = %d\n", 
+                                               (int)count);
+
+                       if(count == 4){
+                               p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+                               if(p_kBuf != NULL){
+                                       p_kBuf[0] = (pTAS2557->mpFirmware->mnTimeStamp&0x000000ff);
+                                       p_kBuf[1] = ((pTAS2557->mpFirmware->mnTimeStamp&0x0000ff00)>>8);
+                                       p_kBuf[2] = ((pTAS2557->mpFirmware->mnTimeStamp&0x00ff0000)>>16);
+                                       p_kBuf[3] = ((pTAS2557->mpFirmware->mnTimeStamp&0xff000000)>>24);
+                                       
+                                       ret = copy_to_user(buf, p_kBuf, count);
+                                       if (0 != ret) {
+                                               /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                                       
+                                       kfree(p_kBuf);
+                               }else{
+                                       dev_err(pTAS2557->dev, "read no mem\n");
+                               }       
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_CALIBRATION:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_CALIBRATION: count = %d\n", 
+                                               (int)count);
+
+                       if(count == 1){
+                               unsigned char curCal = pTAS2557->mnCurrentCalibration;
+                               ret = copy_to_user(buf, &curCal, 1);
+                               if (0 != ret) {
+                                       /* Failed to copy all the data, exit */
+                                       dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                               }       
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_SAMPLERATE:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_SAMPLERATE: count = %d\n", 
+                                               (int)count);
+                       if(count == 4){
+                               p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+                               if(p_kBuf != NULL){                             
+                                       TConfiguration *pConfiguration = 
+                                               &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+
+                                       p_kBuf[0] = (pConfiguration->mnSamplingRate&0x000000ff);
+                                       p_kBuf[1] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8);
+                                       p_kBuf[2] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16);
+                                       p_kBuf[3] = ((pConfiguration->mnSamplingRate&0xff000000)>>24);
+                                       
+                                       ret = copy_to_user(buf, p_kBuf, count);
+                                       if (0 != ret) {
+                                               /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                                       
+                                       kfree(p_kBuf);
+                               }else{
+                                       dev_err(pTAS2557->dev, "read no mem\n");
+                               }       
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_BITRATE:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_BITRATE: count = %d\n", 
+                                               (int)count);
+
+                       if(count == 1){
+                               unsigned int dac_format = 0;
+                               unsigned char bitRate = 0;
+                               ret = pTAS2557->read(pTAS2557, 
+                                       TAS2557_ASI1_DAC_FORMAT_REG, &dac_format);
+                               if(ret >=0){    
+                                       bitRate = (dac_format&0x18)>>3;
+                                       if(bitRate == 0) bitRate = 16;
+                                       else if(bitRate == 1) bitRate = 20;
+                                       else if(bitRate == 2) bitRate = 24;
+                                       else if(bitRate == 3) bitRate = 32;
+                                       ret = copy_to_user(buf, &bitRate, 1);
+                                       if (0 != ret) {
+                                       /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                               }                                       
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_DACVOLUME:
+               {
+                       if(g_logEnable) dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_DACVOLUME: count = %d\n", 
+                                               (int)count);
+
+                       if(count == 1){
+                               unsigned int value = 0;
+                               unsigned char volume = 0;
+                               ret = pTAS2557->read(pTAS2557, 
+                                       TAS2557_SPK_CTRL_REG, &value);
+                               if(ret >=0){    
+                                       volume = (value&0x78)>>3;                                       
+                                       ret = copy_to_user(buf, &volume, 1);
+                                       if (0 != ret) {
+                                       /* Failed to copy all the data, exit */
+                                               dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+                                       }
+                               }                                       
+                       }
+               }
+               break;
+       }
+        pTAS2557->mnDBGCmd = 0;
+
+       mutex_unlock(&pTAS2557->file_lock); 
+       return count;
+}
+
+static ssize_t tas2557_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+       struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+       int ret = 0;
+//     unsigned int value = 0;
+       unsigned char *p_kBuf = NULL;
+       unsigned int reg = 0;
+       unsigned int len = 0;
+
+       mutex_lock(&pTAS2557->file_lock);
+               
+       p_kBuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
+       if(p_kBuf == NULL) {
+               dev_err(pTAS2557->dev, "write no mem\n");
+               goto err;
+       }
+       
+       ret = copy_from_user(p_kBuf, buf, count);
+       if (0 != ret) {
+               dev_err(pTAS2557->dev,"copy_from_user failed.\n");
+               goto err;
+       }
+
+       pTAS2557->mnDBGCmd = p_kBuf[0];
+       switch(pTAS2557->mnDBGCmd)
+       {
+               case TIAUDIO_CMD_REG_WITE:
+               if(count > 5){
+                       reg = ((unsigned int)p_kBuf[1] << 24) + 
+                               ((unsigned int)p_kBuf[2] << 16) + 
+                               ((unsigned int)p_kBuf[3] << 8) + 
+                               (unsigned int)p_kBuf[4];
+                       len = count - 5;
+                       if(len == 1){
+                               ret = pTAS2557->write(pTAS2557, reg, p_kBuf[5]);
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", 
+                                       reg, p_kBuf[5]);
+                       }else{
+                               ret = pTAS2557->bulk_write(pTAS2557, reg, &p_kBuf[5], len);
+                       }
+               }else{
+                       dev_err(pTAS2557->dev,"%s, write len fail, count=%d.\n", 
+                               __FUNCTION__, (int)count);
+               }
+               pTAS2557->mnDBGCmd = 0;
+               break;
+               
+               case TIAUDIO_CMD_REG_READ:
+               if(count == 5){
+                       pTAS2557->mnCurrentReg = ((unsigned int)p_kBuf[1] << 24) + 
+                               ((unsigned int)p_kBuf[2] << 16) + 
+                               ((unsigned int)p_kBuf[3] << 8)  + 
+                               (unsigned int)p_kBuf[4];                
+                       if(g_logEnable){
+                               dev_info(pTAS2557->dev,
+                                       "TIAUDIO_CMD_REG_READ, whole=0x%x\n", 
+                                       pTAS2557->mnCurrentReg);
+                       }       
+               }else{
+                       dev_err(pTAS2557->dev,"read len fail.\n");
+               }                       
+               break;
+               
+               case TIAUDIO_CMD_DEBUG_ON:
+               {
+                       if(count == 2){
+                               g_logEnable = p_kBuf[1];
+                       }
+                       pTAS2557->mnDBGCmd = 0;
+               }
+               break;
+                       
+               case TIAUDIO_CMD_PROGRAM:
+               {
+                       if(count == 2){
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_PROGRAM, set to %d\n", 
+                                       p_kBuf[1]);
+                               tas2557_set_program(pTAS2557, p_kBuf[1]);
+                               pTAS2557->mnDBGCmd = 0;
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_CONFIGURATION:
+               {
+                       if(count == 2){
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_CONFIGURATION, set to %d\n", 
+                                       p_kBuf[1]);
+                               tas2557_set_config(pTAS2557, p_kBuf[1]);
+                               pTAS2557->mnDBGCmd = 0;
+                       }
+               }
+               break;  
+               
+               case TIAUDIO_CMD_FW_TIMESTAMP:
+               /*let go*/
+               break;
+               
+               case TIAUDIO_CMD_CALIBRATION:
+               {
+                       if(count == 2){
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_CALIBRATION, set to %d\n", 
+                                       p_kBuf[1]);
+                               tas2557_set_calibration(pTAS2557, p_kBuf[1]);
+                               pTAS2557->mnDBGCmd = 0;
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_SAMPLERATE:
+               {
+                       if(count == 5){
+                               unsigned int nSampleRate = ((unsigned int)p_kBuf[1] << 24) + 
+                                       ((unsigned int)p_kBuf[2] << 16) + 
+                                       ((unsigned int)p_kBuf[3] << 8)  + 
+                                       (unsigned int)p_kBuf[4];        
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_SAMPLERATE, set to %d\n", 
+                                       nSampleRate);
+
+                               tas2557_set_sampling_rate(pTAS2557, nSampleRate);
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_BITRATE:
+               {
+                       if(count == 2){
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_BITRATE, set to %d\n", 
+                                       p_kBuf[1]);
+
+                               tas2557_set_bit_rate(pTAS2557, p_kBuf[1]);
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_DACVOLUME:
+               {
+                       if(count == 2){
+                               unsigned char volume = (p_kBuf[1] & 0x0f);
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_DACVOLUME, set to %d\n", 
+                                       volume);
+
+                               pTAS2557->update_bits(pTAS2557, 
+                                       TAS2557_SPK_CTRL_REG, 0x78, volume<<3);
+                       }
+               }
+               break;
+               
+               case TIAUDIO_CMD_SPEAKER:
+               {
+                       if(count == 2){
+                               if(g_logEnable)
+                                       dev_info(pTAS2557->dev, 
+                                       "TIAUDIO_CMD_SPEAKER, set to %d\n", 
+                                       p_kBuf[1]);
+                               tas2557_enable(pTAS2557, (p_kBuf[1]>0));
+                       }
+               }
+               break;
+
+               case TIAUDIO_CMD_FW_RELOAD:
+               {
+                       if(count == 1){
+                               ret = request_firmware_nowait(THIS_MODULE, 1, TAS2557_FW_NAME,
+                                       pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready);
+                                       
+                               if(g_logEnable) 
+                                       dev_info(pTAS2557->dev,
+                                               "TIAUDIO_CMD_FW_RELOAD: ret = %d\n", 
+                                               ret);                                   
+                       }
+                               
+               }
+               break;
+               
+               default:
+                       pTAS2557->mnDBGCmd = 0;
+               break;
+       }
+
+err:
+       if(p_kBuf != NULL)
+               kfree(p_kBuf);
+       
+       mutex_unlock(&pTAS2557->file_lock);
+       
+       return count;
+}
+
+static long tas2557_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct tas2557_priv *pTAS2557 = file->private_data;
+       int ret = 0;
+       
+       mutex_lock(&pTAS2557->file_lock);
+               
+       switch (cmd) {
+               case SMARTPA_SPK_DAC_VOLUME:
+               {
+                       u8 volume = (arg & 0x0f);
+                       pTAS2557->update_bits(pTAS2557, 
+                               TAS2557_SPK_CTRL_REG, 0x78, volume<<3);
+               }               
+               break;
+       
+               case SMARTPA_SPK_POWER_ON:
+               {
+                       tas2557_enable(pTAS2557, true);
+               }
+               break;
+               
+               case SMARTPA_SPK_POWER_OFF:
+               {
+                       tas2557_enable(pTAS2557, false);
+               }               
+               break;
+               
+               case SMARTPA_SPK_SWITCH_PROGRAM:
+               {
+                       tas2557_set_program(pTAS2557, arg);
+               }
+               break;
+               
+               case SMARTPA_SPK_SWITCH_CONFIGURATION:
+               {
+                       tas2557_set_config(pTAS2557, arg);
+               }
+               break;
+               
+               case SMARTPA_SPK_SWITCH_CALIBRATION:
+               {
+                       tas2557_set_calibration(pTAS2557, arg);
+               }
+               break;
+               
+               case SMARTPA_SPK_SET_SAMPLERATE:
+               {
+                       tas2557_set_sampling_rate(pTAS2557, arg);
+               }
+               break;          
+
+               case SMARTPA_SPK_SET_BITRATE:
+               {
+                       tas2557_set_bit_rate(pTAS2557, arg);
+               }
+               break;  
+       }
+       
+       mutex_unlock(&pTAS2557->file_lock);
+       return ret;
+}
+
+static struct file_operations fops =
+{
+       .owner = THIS_MODULE,
+       .read = tas2557_file_read,
+       .write = tas2557_file_write,
+       .unlocked_ioctl = tas2557_file_unlocked_ioctl,
+       .open = tas2557_file_open,
+       .release = tas2557_file_release,
+};
+
+#define MODULE_NAME    "tas2557"
+static struct miscdevice tas2557_misc =
+{
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = MODULE_NAME,
+       .fops = &fops,
+};
+
+int tas2557_register_misc(struct tas2557_priv * pTAS2557)
+{
+       int ret = 0;
+       
+       g_tas2557 = pTAS2557;
+       
+       ret = misc_register(&tas2557_misc);
+       if (ret) {
+               dev_err(pTAS2557->dev, "TAS2557 misc fail: %d\n", ret);
+       }
+
+    dev_info(pTAS2557->dev, "%s, leave\n", __FUNCTION__);
+       
+       return ret;
+}
+
+int tas2557_deregister_misc(struct tas2557_priv * pTAS2557)
+{
+       misc_deregister(&tas2557_misc);
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 Misc Smart Amplifier driver");
+MODULE_LICENSE("GPLv2");
+#endif
diff --git a/tas2557-misc.h b/tas2557-misc.h
new file mode 100755 (executable)
index 0000000..a1d95e0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-misc.h
+**
+** Description:
+**     header file for tas2557-misc.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_MISC_H
+#define _TAS2557_MISC_H
+
+#define        FW_NAME_SIZE                    64
+#define        FW_DESCRIPTION_SIZE             256
+#define        PROGRAM_BUF_SIZE                (5 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+#define        CONFIGURATION_BUF_SIZE  (8 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+
+#define        TIAUDIO_CMD_REG_WITE                    1
+#define        TIAUDIO_CMD_REG_READ                    2
+#define        TIAUDIO_CMD_DEBUG_ON                    3
+#define        TIAUDIO_CMD_PROGRAM                             4
+#define        TIAUDIO_CMD_CONFIGURATION               5
+#define        TIAUDIO_CMD_FW_TIMESTAMP                6
+#define        TIAUDIO_CMD_CALIBRATION                 7
+#define        TIAUDIO_CMD_SAMPLERATE                  8
+#define        TIAUDIO_CMD_BITRATE                             9
+#define        TIAUDIO_CMD_DACVOLUME                   10
+#define        TIAUDIO_CMD_SPEAKER                             11
+#define        TIAUDIO_CMD_FW_RELOAD                   12
+
+#define        TAS2557_MAGIC_NUMBER    0x32353535      /* '2557' */
+
+#define        SMARTPA_SPK_DAC_VOLUME                          _IOWR(TAS2557_MAGIC_NUMBER, 1, unsigned long)
+#define        SMARTPA_SPK_POWER_ON                            _IOWR(TAS2557_MAGIC_NUMBER, 2, unsigned long)
+#define        SMARTPA_SPK_POWER_OFF                           _IOWR(TAS2557_MAGIC_NUMBER, 3, unsigned long)
+#define        SMARTPA_SPK_SWITCH_PROGRAM                      _IOWR(TAS2557_MAGIC_NUMBER, 4, unsigned long)
+#define        SMARTPA_SPK_SWITCH_CONFIGURATION        _IOWR(TAS2557_MAGIC_NUMBER, 5, unsigned long)
+#define        SMARTPA_SPK_SWITCH_CALIBRATION          _IOWR(TAS2557_MAGIC_NUMBER, 6, unsigned long)
+#define        SMARTPA_SPK_SET_SAMPLERATE                      _IOWR(TAS2557_MAGIC_NUMBER, 7, unsigned long)
+#define        SMARTPA_SPK_SET_BITRATE                         _IOWR(TAS2557_MAGIC_NUMBER, 8, unsigned long)
+
+extern int tas2557_register_misc(struct tas2557_priv *pTAS2557);
+extern int tas2557_deregister_misc(struct tas2557_priv *pTAS2557);     
+
+#endif /* _TAS2557_MISC_H */
diff --git a/tas2557-regmap.c b/tas2557-regmap.c
new file mode 100755 (executable)
index 0000000..0b42d9a
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557-regmap.c
+**
+** Description:
+**     I2C driver with regmap for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_REGMAP
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include "tas2557.h"
+#include "tas2557-core.h"
+
+#ifdef CONFIG_TAS2557_CODEC
+#include "tas2557-codec.h"
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+#include "tas2557-misc.h"
+#endif
+
+#define ENABLE_TILOAD                  //only enable this for in-system tuning or debug, not for production systems
+#ifdef ENABLE_TILOAD
+#include "tiload.h"
+#endif
+
+static void tas2557_change_book_page(struct tas2557_priv *pTAS2557, int nBook,
+       int nPage)
+{
+       if ((pTAS2557->mnCurrentBook == nBook) 
+               && pTAS2557->mnCurrentPage == nPage){
+               return;
+       }
+
+       if (pTAS2557->mnCurrentBook != nBook) {
+               regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, 0);
+               pTAS2557->mnCurrentPage = 0;
+               regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_REG, nBook);
+               pTAS2557->mnCurrentBook = nBook;
+               if (nPage != 0) {
+                       regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage);
+                       pTAS2557->mnCurrentPage = nPage;
+               }
+       } else if (pTAS2557->mnCurrentPage != nPage) {
+               regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage);
+               pTAS2557->mnCurrentPage = nPage;
+       }
+}
+
+static int tas2557_dev_read(struct tas2557_priv *pTAS2557,
+       unsigned int nRegister, unsigned int *pValue)
+{
+       int ret = 0;
+
+       mutex_lock(&pTAS2557->dev_lock);        
+       
+       if (pTAS2557->mbTILoadActive) {
+               if (!(nRegister & 0x80000000)){
+                       mutex_unlock(&pTAS2557->dev_lock);
+                       return 0;                       // let only reads from TILoad pass.
+               }
+               nRegister &= ~0x80000000;
+       }
+
+/*     
+       dev_dbg(pTAS2557->dev, "%s: BOOK:PAGE:REG %u:%u:%u\n", __func__,
+               TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister),
+               TAS2557_PAGE_REG(nRegister));
+*/
+       tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister),
+               TAS2557_PAGE_ID(nRegister));
+       ret = regmap_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), pValue);
+
+       mutex_unlock(&pTAS2557->dev_lock);
+       return ret;
+}
+
+static int tas2557_dev_write(struct tas2557_priv *pTAS2557,
+       unsigned int nRegister, unsigned int nValue)
+{
+       int ret = 0;
+       
+       mutex_lock(&pTAS2557->dev_lock);
+       if ((nRegister == 0xAFFEAFFE) && (nValue == 0xBABEBABE)) {
+               pTAS2557->mbTILoadActive = true;
+               mutex_unlock(&pTAS2557->dev_lock);
+               return 0;
+       }
+
+       if ((nRegister == 0xBABEBABE) && (nValue == 0xAFFEAFFE)) {
+               pTAS2557->mbTILoadActive = false;
+               mutex_unlock(&pTAS2557->dev_lock);
+               return 0;
+       }
+
+       if (pTAS2557->mbTILoadActive) {
+               if (!(nRegister & 0x80000000)){
+                       mutex_unlock(&pTAS2557->dev_lock);
+                       return 0;                       // let only writes from TILoad pass.
+               }
+               nRegister &= ~0x80000000;
+       }
+
+       tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister),
+               TAS2557_PAGE_ID(nRegister));
+//  dev_err(codec->dev, "%s: BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n",
+//      __func__, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister),
+//      TAS2557_PAGE_REG(nRegister), value);
+       ret = regmap_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister),
+               nValue);
+       mutex_unlock(&pTAS2557->dev_lock);              
+       
+       return ret;
+}
+
+static int tas2557_dev_bulk_read(struct tas2557_priv *pTAS2557,
+       unsigned int nRegister, u8 * pData, unsigned int nLength)
+{
+       int ret = 0;
+       
+       mutex_lock(&pTAS2557->dev_lock);
+       if (pTAS2557->mbTILoadActive) {
+               if (!(nRegister & 0x80000000)){
+                       mutex_unlock(&pTAS2557->dev_lock);
+                       return 0;                       // let only writes from TILoad pass.
+               }
+               nRegister &= ~0x80000000;
+       }
+
+       tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister),
+               TAS2557_PAGE_ID(nRegister));
+       ret = regmap_bulk_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister),
+               pData, nLength);
+       mutex_unlock(&pTAS2557->dev_lock);      
+
+       return ret;
+}
+
+static int tas2557_dev_bulk_write(struct tas2557_priv *pTAS2557,
+       unsigned int nRegister, u8 * pData, unsigned int nLength)
+{
+       int ret = 0;
+       mutex_lock(&pTAS2557->dev_lock);
+       if (pTAS2557->mbTILoadActive) {
+               if (!(nRegister & 0x80000000)){
+                       mutex_unlock(&pTAS2557->dev_lock);
+                       return 0;                       // let only writes from TILoad pass.
+               }
+               nRegister &= ~0x80000000;
+       }
+
+       tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister),
+               TAS2557_PAGE_ID(nRegister));
+       ret = regmap_bulk_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister),
+               pData, nLength);
+       mutex_unlock(&pTAS2557->dev_lock);              
+       
+       return ret;
+}
+
+static int tas2557_dev_update_bits(struct tas2557_priv *pTAS2557,
+       unsigned int nRegister, unsigned int nMask, unsigned int nValue)
+{
+       int ret = 0;
+       
+       mutex_lock(&pTAS2557->dev_lock);
+       
+       if (pTAS2557->mbTILoadActive) {
+               if (!(nRegister & 0x80000000)){
+                       mutex_unlock(&pTAS2557->dev_lock);
+                       return 0;                       // let only writes from TILoad pass.
+               }
+               nRegister &= ~0x80000000;
+       }
+       
+       tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister),
+               TAS2557_PAGE_ID(nRegister));
+       
+       ret = regmap_update_bits(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), nMask, nValue);
+               
+       mutex_unlock(&pTAS2557->dev_lock);              
+       return ret;
+}
+
+static bool tas2557_volatile(struct device *pDev, unsigned int nRegister)
+{
+       return true;
+}
+
+static bool tas2557_writeable(struct device *pDev, unsigned int nRegister)
+{
+       return true;
+}
+
+static const struct regmap_config tas2557_i2c_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .writeable_reg = tas2557_writeable,
+       .volatile_reg = tas2557_volatile,
+       .cache_type = REGCACHE_NONE,
+       .max_register = 128,
+};
+
+static int tas2557_i2c_probe(struct i2c_client *pClient,
+       const struct i2c_device_id *pID)
+{
+       struct tas2557_priv *pTAS2557;
+       int nResult;
+
+       dev_info(&pClient->dev, "%s enter\n", __FUNCTION__);
+       
+       pTAS2557 = devm_kzalloc(&pClient->dev, sizeof(struct tas2557_priv), GFP_KERNEL);
+       if (!pTAS2557)
+               return -ENOMEM;
+
+       pTAS2557->dev = &pClient->dev;
+       i2c_set_clientdata(pClient, pTAS2557);
+       dev_set_drvdata(&pClient->dev, pTAS2557);
+
+       if (pClient->dev.of_node){
+               tas2557_parse_dt(&pClient->dev, pTAS2557);
+       }
+
+       if (gpio_is_valid(pTAS2557->mnResetGPIO)) {
+#ifdef HW_RESET        //mandatory             
+               devm_gpio_request_one(&pClient->dev, pTAS2557->mnResetGPIO,
+                       GPIOF_OUT_INIT_LOW, "TAS2557_RST");
+               msleep(5);
+               gpio_set_value_cansleep(pTAS2557->mnResetGPIO, 1);
+               mdelay(1);
+#endif         
+       }
+
+       pTAS2557->mpRegmap = devm_regmap_init_i2c(pClient, &tas2557_i2c_regmap);
+       if (IS_ERR(pTAS2557->mpRegmap)) {
+               nResult = PTR_ERR(pTAS2557->mpRegmap);
+               dev_err(&pClient->dev, "Failed to allocate register map: %d\n",
+                       nResult);
+               return nResult;
+       }
+
+       pTAS2557->read = tas2557_dev_read;
+       pTAS2557->write = tas2557_dev_write;
+       pTAS2557->bulk_read = tas2557_dev_bulk_read;
+       pTAS2557->bulk_write = tas2557_dev_bulk_write;
+       pTAS2557->update_bits = tas2557_dev_update_bits;
+       pTAS2557->set_config = tas2557_set_config;
+       pTAS2557->set_calibration = tas2557_set_calibration;
+               
+       mutex_init(&pTAS2557->dev_lock);
+       
+       /* Reset the chip */
+       nResult = tas2557_dev_write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+       if(nResult < 0){
+               dev_err(&pClient->dev, "I2C communication ERROR: %d\n",
+                       nResult);
+               return nResult;
+       }
+       
+       udelay(1000);
+
+       pTAS2557->mpFirmware =
+               devm_kzalloc(&pClient->dev, sizeof(TFirmware),
+               GFP_KERNEL);
+       if (!pTAS2557->mpFirmware)
+               return -ENOMEM;
+
+       pTAS2557->mpCalFirmware =
+               devm_kzalloc(&pClient->dev, sizeof(TFirmware),
+               GFP_KERNEL);
+       if (!pTAS2557->mpCalFirmware)
+               return -ENOMEM;
+
+       pTAS2557->mnCurrentPage = 0;
+       pTAS2557->mnCurrentBook = 0;
+
+       nResult = tas2557_dev_read(pTAS2557, TAS2557_REV_PGID_REG, &pTAS2557->mnPGID);
+       dev_info(&pClient->dev, "TAS2557 PGID: 0x%02x\n", pTAS2557->mnPGID);
+
+       pTAS2557->mbTILoadActive = false;
+
+#ifdef CONFIG_TAS2557_CODEC    
+       mutex_init(&pTAS2557->codec_lock);
+       tas2557_register_codec(pTAS2557);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC     
+       mutex_init(&pTAS2557->file_lock);
+       tas2557_register_misc(pTAS2557);
+#endif
+
+#ifdef ENABLE_TILOAD
+       tiload_driver_init(pTAS2557);
+#endif
+
+       nResult = request_firmware_nowait(THIS_MODULE, 1, TAS2557_FW_NAME,
+               pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready);
+               
+       return nResult;
+}
+
+static int tas2557_i2c_remove(struct i2c_client *pClient)
+{
+       struct tas2557_priv *pTAS2557 = i2c_get_clientdata(pClient);
+       
+       dev_info(pTAS2557->dev, "%s\n", __FUNCTION__);
+       
+#ifdef CONFIG_TAS2557_CODEC            
+       tas2557_deregister_codec(pTAS2557);
+       mutex_destroy(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC             
+       tas2557_deregister_misc(pTAS2557);
+       mutex_destroy(&pTAS2557->file_lock);
+#endif
+       
+       return 0;
+}
+
+static const struct i2c_device_id tas2557_i2c_id[] = {
+       {"tas2557", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tas2557_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2557_of_match[] = {
+       {.compatible = "ti,tas2557"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, tas2557_of_match);
+#endif
+
+static struct i2c_driver tas2557_i2c_driver = {
+       .driver = {
+                       .name = "tas2557",
+                       .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+                       .of_match_table = of_match_ptr(tas2557_of_match),
+#endif
+               },
+       .probe = tas2557_i2c_probe,
+       .remove = tas2557_i2c_remove,
+       .id_table = tas2557_i2c_id,
+};
+
+module_i2c_driver(tas2557_i2c_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPLv2");
+#endif
\ No newline at end of file
diff --git a/tas2557.h b/tas2557.h
new file mode 100755 (executable)
index 0000000..a25d3ac
--- /dev/null
+++ b/tas2557.h
@@ -0,0 +1,392 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tas2557.h
+**
+** Description:
+**     definitions and data structures for TAS2557 Android Linux driver
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_H
+#define _TAS2557_H
+
+/* Page Control Register */
+#define TAS2557_PAGECTL_REG                    0
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2557_BOOKCTL_PAGE                   0
+#define TAS2557_BOOKCTL_REG                    127
+
+#define TAS2557_REG(book, page, reg)           (((book * 256 * 128) + \
+                                                (page * 128)) + reg)
+
+#define TAS2557_BOOK_ID(reg)                   (reg / (256 * 128))
+#define TAS2557_PAGE_ID(reg)                   ((reg % (256 * 128)) / 128)
+#define TAS2557_BOOK_REG(reg)                  (reg % (256 * 128))
+#define TAS2557_PAGE_REG(reg)                  ((reg % (256 * 128)) % 128)
+
+/* Book0, Page0 registers */
+#define TAS2557_SW_RESET_REG                   TAS2557_REG(0, 0, 1)
+
+#define TAS2557_REV_PGID_REG                   TAS2557_REG(0, 0, 3)
+#define TAS2557_PG_VERSION_1P0                 0x00
+#define TAS2557_PG_VERSION_2P0                 0x10
+#define TAS2557_PG_VERSION_2P1                 0x11
+
+#define TAS2557_POWER_CTRL1_REG                        TAS2557_REG(0, 0, 4)
+#define TAS2557_POWER_CTRL2_REG                        TAS2557_REG(0, 0, 5)
+#define TAS2557_SPK_CTRL_REG                   TAS2557_REG(0, 0, 6)
+#define TAS2557_MUTE_REG                               TAS2557_REG(0, 0, 7)
+#define TAS2557_SNS_CTRL_REG                   TAS2557_REG(0, 0, 8)
+#define TAS2557_ADC_INPUT_SEL_REG              TAS2557_REG(0, 0, 9)
+#define TAS2557_DRBOOST_CTL_REG                        TAS2557_REG(0, 0, 10)
+#define TAS2557_NONAME11_REG                   TAS2557_REG(0, 0, 11)
+#define TAS2557_NONAME12_REG                   TAS2557_REG(0, 0, 12)
+#define TAS2557_NONAME13_REG                   TAS2557_REG(0, 0, 13)
+#define TAS2557_NONAME14_REG                   TAS2557_REG(0, 0, 14)
+#define TAS2557_NONAME15_REG                   TAS2557_REG(0, 0, 15)
+#define TAS2557_NONAME16_REG                   TAS2557_REG(0, 0, 16)
+#define TAS2557_NONAME17_REG                   TAS2557_REG(0, 0, 17)
+#define TAS2557_NONAME18_REG                   TAS2557_REG(0, 0, 18)
+#define TAS2557_SAR_SAMPLING_TIME_REG          TAS2557_REG(0, 0, 19)
+#define TAS2557_SAR_ADC1_REG                   TAS2557_REG(0, 0, 20)
+#define TAS2557_SAR_ADC2_REG                   TAS2557_REG(0, 0, 21)
+#define TAS2557_CRC_CHECKSUM_REG               TAS2557_REG(0, 0, 32)
+#define TAS2557_CRC_RESET_REG                  TAS2557_REG(0, 0, 33)
+#define TAS2557_DSP_MODE_SELECT_REG            TAS2557_REG(0, 0, 34)
+#define TAS2557_NONAME42_REG                   TAS2557_REG(0, 0, 42)
+#define TAS2557_CLK_ERR_CTRL                   TAS2557_REG(0, 0, 44)
+#define TAS2557_DRBOOST_CFG_REG                        TAS2557_REG(0, 0, 52)
+#define TAS2557_POWER_UP_FLAG_REG              TAS2557_REG(0, 0, 100)
+#define TAS2557_FLAGS_1                                TAS2557_REG(0, 0, 104)
+#define TAS2557_FLAGS_2                                TAS2557_REG(0, 0, 108)
+/* Book0, Page1 registers */
+#define TAS2557_ASI1_DAC_FORMAT_REG            TAS2557_REG(0, 1, 1)
+#define TAS2557_ASI1_ADC_FORMAT_REG            TAS2557_REG(0, 1, 2)
+#define TAS2557_ASI1_OFFSET1_REG               TAS2557_REG(0, 1, 3)
+#define TAS2557_ASI1_ADC_PATH_REG              TAS2557_REG(0, 1, 7)
+#define TAS2557_ASI1_DAC_BCLK_REG              TAS2557_REG(0, 1, 8)
+#define TAS2557_ASI1_DAC_WCLK_REG              TAS2557_REG(0, 1, 9)
+#define TAS2557_ASI1_ADC_BCLK_REG              TAS2557_REG(0, 1, 10)
+#define TAS2557_ASI1_ADC_WCLK_REG              TAS2557_REG(0, 1, 11)
+#define TAS2557_ASI1_DIN_DOUT_MUX_REG          TAS2557_REG(0, 1, 12)
+#define TAS2557_ASI1_BDIV_CLK_SEL_REG          TAS2557_REG(0, 1, 13)
+#define TAS2557_ASI1_BDIV_CLK_RATIO_REG                TAS2557_REG(0, 1, 14)
+#define TAS2557_ASI1_WDIV_CLK_RATIO_REG                TAS2557_REG(0, 1, 15)
+#define TAS2557_ASI1_DAC_CLKOUT_REG            TAS2557_REG(0, 1, 16)
+#define TAS2557_ASI1_ADC_CLKOUT_REG            TAS2557_REG(0, 1, 17)
+
+#define TAS2557_ASI2_DAC_FORMAT_REG            TAS2557_REG(0, 1, 21)
+#define TAS2557_ASI2_ADC_FORMAT_REG            TAS2557_REG(0, 1, 22)
+#define TAS2557_ASI2_OFFSET1_REG               TAS2557_REG(0, 1, 23)
+#define TAS2557_ASI2_ADC_PATH_REG              TAS2557_REG(0, 1, 27)
+#define TAS2557_ASI2_DAC_BCLK_REG              TAS2557_REG(0, 1, 28)
+#define TAS2557_ASI2_DAC_WCLK_REG              TAS2557_REG(0, 1, 29)
+#define TAS2557_ASI2_ADC_BCLK_REG              TAS2557_REG(0, 1, 30)
+#define TAS2557_ASI2_ADC_WCLK_REG              TAS2557_REG(0, 1, 31)
+#define TAS2557_ASI2_DIN_DOUT_MUX_REG          TAS2557_REG(0, 1, 32)
+#define TAS2557_ASI2_BDIV_CLK_SEL_REG          TAS2557_REG(0, 1, 33)
+#define TAS2557_ASI2_BDIV_CLK_RATIO_REG                TAS2557_REG(0, 1, 34)
+#define TAS2557_ASI2_WDIV_CLK_RATIO_REG                TAS2557_REG(0, 1, 35)
+#define TAS2557_ASI2_DAC_CLKOUT_REG            TAS2557_REG(0, 1, 36)
+#define TAS2557_ASI2_ADC_CLKOUT_REG            TAS2557_REG(0, 1, 37)
+
+#define TAS2557_GPIO1_PIN_REG                  TAS2557_REG(0, 1, 61)
+#define TAS2557_GPIO2_PIN_REG                  TAS2557_REG(0, 1, 62)
+#define TAS2557_GPIO3_PIN_REG                  TAS2557_REG(0, 1, 63)
+#define TAS2557_GPIO4_PIN_REG                  TAS2557_REG(0, 1, 64)
+#define TAS2557_GPIO5_PIN_REG                  TAS2557_REG(0, 1, 65)
+#define TAS2557_GPIO6_PIN_REG                  TAS2557_REG(0, 1, 66)
+#define TAS2557_GPIO7_PIN_REG                  TAS2557_REG(0, 1, 67)
+#define TAS2557_GPIO8_PIN_REG                  TAS2557_REG(0, 1, 68)
+#define TAS2557_GPIO9_PIN_REG                  TAS2557_REG(0, 1, 69)
+#define TAS2557_GPIO10_PIN_REG                 TAS2557_REG(0, 1, 70)
+
+#define TAS2557_GPI_PIN_REG                    TAS2557_REG(0, 1, 77)
+#define TAS2557_GPIO_HIZ_CTRL1_REG             TAS2557_REG(0, 1, 79)
+#define TAS2557_GPIO_HIZ_CTRL2_REG             TAS2557_REG(0, 1, 80)
+#define TAS2557_GPIO_HIZ_CTRL3_REG             TAS2557_REG(0, 1, 81)
+#define TAS2557_GPIO_HIZ_CTRL4_REG             TAS2557_REG(0, 1, 82)
+#define TAS2557_GPIO_HIZ_CTRL5_REG             TAS2557_REG(0, 1, 83)
+
+#define TAS2557_BIT_BANG_CTRL_REG              TAS2557_REG(0, 1, 87)
+#define TAS2557_BIT_BANG_OUT1_REG              TAS2557_REG(0, 1, 88)
+#define TAS2557_BIT_BANG_OUT2_REG              TAS2557_REG(0, 1, 89)
+#define TAS2557_BIT_BANG_IN1_REG               TAS2557_REG(0, 1, 90)
+#define TAS2557_BIT_BANG_IN2_REG               TAS2557_REG(0, 1, 91)
+#define TAS2557_BIT_BANG_IN3_REG               TAS2557_REG(0, 1, 92)
+
+#define TAS2557_PDM_IN_CLK_REG                 TAS2557_REG(0, 1, 94)
+#define TAS2557_PDM_IN_PIN_REG                 TAS2557_REG(0, 1, 95)
+
+#define TAS2557_ASIM_IFACE1_REG                        TAS2557_REG(0, 1, 98)
+#define TAS2557_ASIM_FORMAT_REG                        TAS2557_REG(0, 1, 99)
+#define TAS2557_ASIM_IFACE3_REG                        TAS2557_REG(0, 1, 100)
+#define TAS2557_ASIM_IFACE4_REG                        TAS2557_REG(0, 1, 101)
+#define TAS2557_ASIM_IFACE5_REG                        TAS2557_REG(0, 1, 102)
+#define TAS2557_ASIM_IFACE6_REG                        TAS2557_REG(0, 1, 103)
+#define TAS2557_ASIM_IFACE7_REG                        TAS2557_REG(0, 1, 104)
+#define TAS2557_ASIM_IFACE8_REG                        TAS2557_REG(0, 1, 105)
+#define TAS2557_ASIM_IFACE9_REG                        TAS2557_REG(0, 1, 106)
+
+#define TAS2557_MAIN_CLKIN_REG                 TAS2557_REG(0, 1, 115)
+#define TAS2557_PLL_CLKIN_REG                  TAS2557_REG(0, 1, 116)
+#define TAS2557_CLKOUT_MUX_REG                 TAS2557_REG(0, 1, 117)
+#define TAS2557_CLKOUT_CDIV_REG                        TAS2557_REG(0, 1, 118)
+
+#define TAS2557_HACK_GP01_REG                  TAS2557_REG(0, 1, 122)
+#define TAS2557_HACK01_REG                     TAS2557_REG(0, 2, 10)
+
+#define TAS2557_ISENSE_THRESHOLD               TAS2557_REG(0, 50, 104)
+#define TAS2557_BOOSTON_EFFICIENCY             TAS2557_REG(0, 51, 16)
+#define TAS2557_BOOSTOFF_EFFICIENCY            TAS2557_REG(0, 51, 20)
+#define TAS2557_BOOST_HEADROOM                 TAS2557_REG(0, 51, 24)
+#define TAS2557_THERMAL_FOLDBACK_REG   TAS2557_REG(0, 51, 100)
+#define TAS2557_VPRED_COMP_REG                 TAS2557_REG(0, 53, 24)
+
+#define TAS2557_TEST_MODE_REG                  TAS2557_REG(0, 253, 13)
+#define TAS2557_CRYPTIC_REG                    TAS2557_REG(0, 253, 71)
+
+//#define TAS2557__REG      TAS2557_REG(0, 1, )
+//#define TAS2557__REG      TAS2557_REG(1, 0, )
+#define TAS2557_DAC_INTERPOL_REG               TAS2557_REG(100, 0, 1)
+#define TAS2557_SOFT_MUTE_REG                  TAS2557_REG(100, 0, 7)
+#define TAS2557_PLL_P_VAL_REG                  TAS2557_REG(100, 0, 27)
+#define TAS2557_PLL_J_VAL_REG                  TAS2557_REG(100, 0, 28)
+#define TAS2557_PLL_D_VAL_MSB_REG              TAS2557_REG(100, 0, 29)
+#define TAS2557_PLL_D_VAL_LSB_REG              TAS2557_REG(100, 0, 30)
+#define TAS2557_CLK_MISC_REG                   TAS2557_REG(100, 0, 31)
+#define TAS2557_PLL_N_VAL_REG                  TAS2557_REG(100, 0, 32)
+#define TAS2557_DAC_MADC_VAL_REG               TAS2557_REG(100, 0, 33)
+#define TAS2557_ISENSE_DIV_REG                 TAS2557_REG(100, 0, 42)
+#define TAS2557_RAMP_CLK_DIV_MSB_REG           TAS2557_REG(100, 0, 43)
+#define TAS2557_RAMP_CLK_DIV_LSB_REG           TAS2557_REG(100, 0, 44)
+/* Bits */
+/* B0P0R4 - TAS2557_POWER_CTRL1_REG */
+#define TAS2557_SW_SHUTDOWN                    (0x1 << 0)
+#define TAS2557_MADC_POWER_UP                  (0x1 << 3)
+#define TAS2557_MDAC_POWER_UP                  (0x1 << 4)
+#define TAS2557_NDIV_POWER_UP                  (0x1 << 5)
+#define TAS2557_PLL_POWER_UP                   (0x1 << 6)
+#define TAS2557_DSP_POWER_UP                   (0x1 << 7)
+
+/* B0P0R5 - TAS2557_POWER_CTRL2_REG */
+#define TAS2557_VSENSE_ENABLE                  (0x1 << 0)
+#define TAS2557_ISENSE_ENABLE                  (0x1 << 1)
+#define TAS2557_BOOST_ENABLE                   (0x1 << 5)
+#define TAS2557_CLASSD_ENABLE                  (0x1 << 7)
+
+/* B0P0R6 - TAS2557_SPK_CTRL_REG */
+#define TAS2557_DAC_GAIN_MASK                  (0xf << 3)
+
+/* B0P0R7 - TAS2557_MUTE_REG */
+#define TAS2557_CLASSD_MUTE                    (0x1 << 0)
+#define TAS2557_ISENSE_MUTE                    (0x1 << 1)
+
+/* B0P253R13 - TAS2557_TEST_MODE_REG */
+#define TAS2557_TEST_MODE_ENABLE               (13)
+#define TAS2557_TEST_MODE_MASK                 (0xf << 0)
+
+/* B0P253R71 - TAS2557_CRYPTIC_REG */
+#define TAS2557_OSC_TRIM_CAP(x)                        ((x & 0x3f) << 0)
+#define TAS2557_DISABLE_ENCRYPTION             (0x1 << 6)
+#define TAS2557_SL_COMP                                (0x1 << 7)
+
+/* B0P1R115/6 - TAS2557_MAIN/PLL_CLKIN_REG */
+#define TAS2557_XXX_CLKIN_GPIO1                        (0)
+#define TAS2557_XXX_CLKIN_GPIO2                        (1)
+#define TAS2557_XXX_CLKIN_GPIO3                        (2)
+#define TAS2557_XXX_CLKIN_GPIO4                        (3)
+#define TAS2557_XXX_CLKIN_GPIO5                        (4)
+#define TAS2557_XXX_CLKIN_GPIO6                        (5)
+#define TAS2557_XXX_CLKIN_GPIO7                        (6)
+#define TAS2557_XXX_CLKIN_GPIO8                        (7)
+#define TAS2557_XXX_CLKIN_GPIO9                        (8)
+#define TAS2557_XXX_CLKIN_GPIO10               (9)
+#define TAS2557_XXX_CLKIN_GPI1                 (12)
+#define TAS2557_XXX_CLKIN_GPI2                 (13)
+#define TAS2557_XXX_CLKIN_GPI3                 (14)
+#define TAS2557_NDIV_CLKIN_PLL                 (15)
+#define TAS2557_PLL_CLKIN_INT_OSC              (15)
+
+#define TAS2557_MCLK_CLKIN_SRC_GPIO1       (0)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO2       (1)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO3       (2)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO4       (3)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO5       (4)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO6       (5)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO7       (6)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO8       (7)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO9       (8)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO10      (9)
+#define TAS2557_MCLK_CLKIN_SRC_GPI1        (12)
+#define TAS2557_MCLK_CLKIN_SRC_GPI2        (13)
+#define TAS2557_MCLK_CLKIN_SRC_GPI3        (14)
+
+#define TAS2557_FORMAT_I2S                     (0x0 << 5)
+#define TAS2557_FORMAT_DSP                     (0x1 << 5)
+#define TAS2557_FORMAT_RIGHT_J                 (0x2 << 5)
+#define TAS2557_FORMAT_LEFT_J                  (0x3 << 5)
+#define TAS2557_FORMAT_MONO_PCM                        (0x4 << 5)
+#define TAS2557_FORMAT_MASK                    (0x7 << 5)
+
+#define TAS2557_WORDLENGTH_16BIT               (0x0 << 3)
+#define TAS2557_WORDLENGTH_20BIT               (0x1 << 3)
+#define TAS2557_WORDLENGTH_24BIT               (0x2 << 3)
+#define TAS2557_WORDLENGTH_32BIT               (0x3 << 3)
+#define TAS2557_WORDLENGTH_MASK                        TAS2557_WORDLENGTH_32BIT
+
+/* B100P0R7 - TAS2557_SOFT_MUTE_REG */
+#define TAS2557_PDM_SOFT_MUTE                  (0x1 << 0)
+#define TAS2557_VSENSE_SOFT_MUTE               (0x1 << 1)
+#define TAS2557_ISENSE_SOFT_MUTE               (0x1 << 2)
+#define TAS2557_CLASSD_SOFT_MUTE               (0x1 << 3)
+
+/* B100P0R27 - TAS2557_PLL_P_VAL_REG */
+#define TAS2557_PLL_P_VAL_MASK                 (0x3f << 0)
+
+/* B100P0R28 - TAS2557_PLL_J_VAL_REG */
+#define TAS2557_PLL_J_VAL_MASK                 ((unsigned int ) (0x7f << 0))
+#define TAS2557_PLL_J_VAL_MASKX        0x00
+
+/* B100P0R29-30 - TAS2557_PLL_D_VAL_MSB/LSB_REG */
+#define TAS2557_PLL_D_MSB_VAL(x)               ((x >> 8) & 0x3f)
+#define TAS2557_PLL_D_LSB_VAL(x)               (x & 0xff)
+
+/* B100P0R31 - TAS2557_CLK_MISC_REG */
+#define TAS2557_DSP_CLK_FROM_PLL               (0x1 << 5)
+
+#define TAS2557_FW_NAME     "tas2557_uCDSP.bin"
+
+typedef struct {
+       unsigned int mnType;
+       unsigned int mnCommands;
+       unsigned char *mpData;
+} TBlock;
+
+typedef struct {
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnBlocks;
+       TBlock *mpBlocks;
+} TData;
+
+typedef struct {
+       char mpName[64];
+       char *mpDescription;
+       unsigned char mnAppMode;
+       unsigned short mnBoost;
+       TData mData;
+} TProgram;
+
+typedef struct {
+       char mpName[64];
+       char *mpDescription;
+       TBlock mBlock;
+} TPLL;
+
+typedef struct {
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnProgram;
+       unsigned int mnPLL;
+       unsigned int mnSamplingRate;
+       TData mData;
+} TConfiguration;
+
+typedef struct
+{
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnProgram;
+       unsigned int mnConfiguration;
+       TBlock mBlock;
+} TCalibration;
+
+typedef struct {
+       unsigned int mnFWSize;
+       unsigned int mnChecksum;
+       unsigned int mnPPCVersion;
+       unsigned int mnFWVersion;
+       unsigned int mnDriverVersion;
+       unsigned int mnTimeStamp;
+       char mpDDCName[64];
+       char *mpDescription;
+       unsigned int mnDeviceFamily;
+       unsigned int mnDevice;
+       unsigned int mnPLLs;
+       TPLL *mpPLLs;
+       unsigned int mnPrograms;
+       TProgram *mpPrograms;
+       unsigned int mnConfigurations;
+       TConfiguration *mpConfigurations;
+       unsigned int mnCalibrations;
+       TCalibration *mpCalibrations;
+} TFirmware;
+
+struct tas2557_register {
+       int book;
+       int page;
+       int reg;
+};
+
+struct tas2557_priv {
+       struct device *dev;
+       struct regmap *mpRegmap;
+       int mnLoad;
+       int mnPGID;
+       int mnResetGPIO;        
+       struct mutex dev_lock;  
+       TFirmware *mpFirmware;
+       TFirmware *mpCalFirmware;
+       unsigned int mnCurrentProgram;
+       unsigned int mnCurrentSampleRate;
+       unsigned int mnCurrentConfiguration;
+       unsigned int mnCurrentCalibration;
+       int mnCurrentBook;
+       int mnCurrentPage;
+       bool mbTILoadActive;
+       bool mbPowerUp;
+       bool mbLoadConfigurationPostPowerUp;
+       bool mbLoadCalibrationPostPowerUp;
+       unsigned int mnPowerCtrl;
+       bool mbCalibrationLoaded;
+       int (*read) (struct tas2557_priv * pTAS2557, unsigned int reg,
+               unsigned int *pValue);
+       int (*write) (struct tas2557_priv * pTAS2557, unsigned int reg,
+               unsigned int Value);
+       int (*bulk_read) (struct tas2557_priv * pTAS2557, unsigned int reg,
+               unsigned char *pData, unsigned int len);
+       int (*bulk_write) (struct tas2557_priv * pTAS2557, unsigned int reg,
+               unsigned char *pData, unsigned int len);
+       int (*update_bits) (struct tas2557_priv * pTAS2557, unsigned int reg,
+               unsigned int mask, unsigned int value);
+       int (*set_config) (struct tas2557_priv *pTAS2557, int config);
+       int (*set_calibration) (struct tas2557_priv *pTAS2557, int calibration);        
+#ifdef CONFIG_TAS2557_CODEC    
+       struct mutex codec_lock;
+#endif 
+#ifdef CONFIG_TAS2557_MISC     
+       int mnDBGCmd;
+       int mnCurrentReg;       
+       struct mutex file_lock;
+#endif 
+};
+
+#endif /* _TAS2557_H */
diff --git a/tiload.c b/tiload.c
new file mode 100755 (executable)
index 0000000..c794b73
--- /dev/null
+++ b/tiload.c
@@ -0,0 +1,402 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tiload.c
+**
+** Description:
+**     utility for TAS2557 Android in-system tuning
+**
+** =============================================================================
+*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+
+#include "tiload.h"
+
+/* enable debug prints in the driver */
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintk(x...)   printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+//#ifdef TI_LOAD
+
+/* Function prototypes */
+#ifdef REG_DUMP
+static void dump_page(struct i2c_client *i2c, u8 page);
+#endif
+
+/* externs */
+//extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page);
+//extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book);
+//extern int aic3262_write(struct snd_soc_codec *codec, unsigned int reg,
+//             unsigned int value);
+
+static struct cdev *tiload_cdev;
+static int tiload_major = 0;   /* Dynamic allocation of Mjr No. */
+static int tiload_opened = 0;  /* Dynamic allocation of Mjr No. */
+static struct tas2557_priv *g_TAS2557;
+struct class *tiload_class;
+static unsigned int magic_num = 0x00;
+//static int (*codec_write) (struct snd_soc_codec *, unsigned int, unsigned int) = 0;
+//static unsigned int (*codec_read) (struct snd_soc_codec *, unsigned int) = 0; 
+
+static char gPage = 0;
+static char gBook = 0;
+/******************************** Debug section *****************************/
+
+#ifdef REG_DUMP
+/*
+ *----------------------------------------------------------------------------
+ * Function : dump_page
+ * Purpose  : Read and display one codec register page, for debugging purpose
+ *----------------------------------------------------------------------------
+ */
+static void dump_page(struct i2c_client *i2c, u8 page)
+{
+/*
+    int i;
+    u8 data;
+    u8 test_page_array[8];
+
+    dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+//    aic3262_change_page(codec, page);
+
+    data = 0x0;
+
+    i2c_master_send(i2c, data, 1);
+    i2c_master_recv(i2c, test_page_array, 8);
+
+    printk("\n------- aic3262 PAGE %d DUMP --------\n", page);
+    for (i = 0; i < 8; i++) {
+        printk(" [ %d ] = 0x%x\n", i, test_page_array[i]);
+    }
+*/
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_open
+ *
+ * Purpose  : open method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_open(struct inode *in, struct file *filp)
+{
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+       if (tiload_opened) {
+               printk("%s device is already opened\n", "tiload");
+               printk("%s: only one instance of driver is allowed\n", "tiload");
+               return -1;
+       }
+       tiload_opened++;
+       return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_release
+ *
+ * Purpose  : close method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_release(struct inode *in, struct file *filp)
+{
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+       tiload_opened--;
+       return 0;
+}
+
+#define MAX_LENGTH 128
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_read
+ *
+ * Purpose  : read from codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_read(struct file *file, char __user * buf,
+       size_t count, loff_t * offset)
+{
+       static char rd_data[MAX_LENGTH + 1];
+       unsigned int nCompositeRegister = 0, Value;
+       //unsigned int n;
+       char reg_addr;
+       size_t size;
+       int ret = 0;
+#ifdef DEBUG
+       int i;
+#endif
+//    struct i2c_client *i2c = g_codec->control_data;
+
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+       if (count > MAX_LENGTH) {
+               printk("Max %d bytes can be read\n", MAX_LENGTH);
+               return -1;
+       }
+
+       /* copy register address from user space  */
+       size = copy_from_user(&reg_addr, buf, 1);
+       if (size != 0) {
+               printk("read: copy_from_user failure\n");
+               return -1;
+       }
+
+       size = count;
+
+       nCompositeRegister = BPR_REG(gBook, gPage, reg_addr);
+       if (count == 1) {
+               ret =
+                       g_TAS2557->read(g_TAS2557, 0x80000000 | nCompositeRegister, &Value);
+               if (ret >= 0)
+                       rd_data[0] = (char) Value;
+       } else if (count > 1) {
+               ret =
+                       g_TAS2557->bulk_read(g_TAS2557, 0x80000000 | nCompositeRegister,
+                       rd_data, size);
+       }
+       if (ret < 0)
+               printk("%s, %d, ret=%d, count=%zu error happen!\n", __FUNCTION__,
+                       __LINE__, ret, count);
+//    size = i2c_master_recv(i2c, rd_data, count);
+#ifdef DEBUG
+       printk(KERN_ERR "read size = %d, reg_addr= %x , count = %d\n",
+               (int) size, reg_addr, (int) count);
+       for (i = 0; i < (int) size; i++) {
+               printk(KERN_ERR "rd_data[%d]=%x\n", i, rd_data[i]);
+       }
+#endif
+       if (size != count) {
+               printk("read %d registers from the codec\n", (int) size);
+       }
+
+       if (copy_to_user(buf, rd_data, size) != 0) {
+               dprintk("copy_to_user failed\n");
+               return -1;
+       }
+
+       return size;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_write
+ *
+ * Purpose  : write to codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_write(struct file *file, const char __user * buf,
+       size_t count, loff_t * offset)
+{
+       static char wr_data[MAX_LENGTH + 1];
+       char *pData = wr_data;
+       size_t size;
+       unsigned int nCompositeRegister = 0;
+//    u8 pg_no;
+//    unsigned int n;   
+       unsigned int nRegister;
+       int ret = 0;
+#ifdef DEBUG
+       int i;
+#endif
+//    struct i2c_client *i2c = g_codec->control_data;
+
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+
+       if (count > MAX_LENGTH) {
+               printk("Max %d bytes can be read\n", MAX_LENGTH);
+               return -1;
+       }
+
+       /* copy buffer from user space  */
+       size = copy_from_user(wr_data, buf, count);
+       if (size != 0) {
+               printk("copy_from_user failure %d\n", (int) size);
+               return -1;
+       }
+#ifdef DEBUG
+       printk(KERN_ERR "write size = %zu\n", count);
+       for (i = 0; i < (int) count; i++) {
+
+               printk(KERN_INFO "\nwr_data[%d]=%x\n", i, wr_data[i]);
+       }
+#endif
+       nRegister = wr_data[0];
+       size = count;
+       if ((nRegister == 127) && (gPage == 0)) {
+               gBook = wr_data[1];
+               return size;
+       }
+
+       if (nRegister == 0) {
+               gPage = wr_data[1];
+               pData++;
+               count--;
+       }
+#if 1
+       nCompositeRegister = BPR_REG(gBook, gPage, nRegister);
+       if (count == 2) {
+               ret =
+                       g_TAS2557->write(g_TAS2557, 0x80000000 | nCompositeRegister,
+                       pData[1]);
+       } else if (count > 2) {
+               ret =
+                       g_TAS2557->bulk_write(g_TAS2557, 0x80000000 | nCompositeRegister,
+                       &pData[1], count - 1);
+       }
+       if (ret < 0)
+               printk("%s, %d, ret=%d, count=%zu, ERROR Happen\n", __FUNCTION__,
+                       __LINE__, ret, count);
+#else
+       for (n = 1; n < count; n++) {
+               nCompositeRegister = BPR_REG(gBook, gPage, nRegister + n - 1);
+               g_codec->driver->write(g_codec, 0x80000000 | nCompositeRegister,
+                       pData[n]);
+       }
+#endif
+
+       return size;
+}
+
+static void tiload_route_IO(unsigned int bLock)
+{
+       if (bLock) {
+               g_TAS2557->write(g_TAS2557, 0xAFFEAFFE, 0xBABEBABE);
+       } else {
+               g_TAS2557->write(g_TAS2557, 0xBABEBABE, 0xAFFEAFFE);
+       }
+}
+
+static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       long num = 0;
+       void __user *argp = (void __user *) arg;
+       int val;
+
+       BPR bpr;
+
+       printk(KERN_ERR "tiload_ioctl\n\r");
+//    if (_IOC_TYPE(cmd) != TILOAD_IOC_MAGIC)
+//        return -ENOTTY;
+
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+       switch (cmd) {
+       case TILOAD_IOMAGICNUM_GET:
+               num = copy_to_user(argp, &magic_num, sizeof(int));
+               break;
+       case TILOAD_IOMAGICNUM_SET:
+               num = copy_from_user(&magic_num, argp, sizeof(int));
+               tiload_route_IO(magic_num);
+               break;
+       case TILOAD_BPR_READ:
+               break;
+       case TILOAD_BPR_WRITE:
+               num = copy_from_user(&bpr, argp, sizeof(BPR));
+               printk("TILOAD_BPR_WRITE: 0x%02X, 0x%02X, 0x%02X\n\r", bpr.nBook,
+                       bpr.nPage, bpr.nRegister);
+               break;
+       case TILOAD_IOCTL_SET_CONFIG:
+               num = copy_from_user(&val, argp, sizeof(val));
+               g_TAS2557->set_config(g_TAS2557, val);
+               break;
+       case TILOAD_IOCTL_SET_CALIBRATION:
+               num = copy_from_user(&val, argp, sizeof(val));
+               g_TAS2557->set_calibration(g_TAS2557, val);
+               break;                          
+       default:
+               break;
+       }
+       return num;
+}
+
+/*********** File operations structure for tiload *************/
+static struct file_operations tiload_fops = {
+       .owner = THIS_MODULE,
+       .open = tiload_open,
+       .release = tiload_release,
+       .read = tiload_read,
+       .write = tiload_write,
+       .unlocked_ioctl = tiload_ioctl,
+};
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_driver_init
+ *
+ * Purpose  : Register a char driver for dynamic tiload programming
+ *----------------------------------------------------------------------------
+ */
+int tiload_driver_init(struct tas2557_priv *pTAS2557)
+{
+       int result;
+
+       dev_t dev = MKDEV(tiload_major, 0);
+       dprintk("TiLoad DRIVER : %s\n", __FUNCTION__);
+       g_TAS2557 = pTAS2557;
+
+       dprintk("allocating dynamic major number\n");
+
+       result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
+       if (result < 0) {
+               dprintk("cannot allocate major number %d\n", tiload_major);
+               return result;
+       }
+       tiload_class = class_create(THIS_MODULE, DEVICE_NAME);
+       tiload_major = MAJOR(dev);
+       dprintk("allocated Major Number: %d\n", tiload_major);
+
+       tiload_cdev = cdev_alloc();
+       cdev_init(tiload_cdev, &tiload_fops);
+       tiload_cdev->owner = THIS_MODULE;
+       tiload_cdev->ops = &tiload_fops;
+
+       if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL)
+               dprintk(KERN_ERR "Device creation failed\n");
+
+       if (cdev_add(tiload_cdev, dev, 1) < 0) {
+               dprintk("tiload_driver: cdev_add failed \n");
+               unregister_chrdev_region(dev, 1);
+               tiload_cdev = NULL;
+               return 1;
+       }
+       printk("Registered TiLoad driver, Major number: %d \n", tiload_major);
+       //class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0);
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Utility for TAS2557 Android in-system tuning");
+MODULE_LICENSE("GPLv2");
+//#endif
diff --git a/tiload.h b/tiload.h
new file mode 100755 (executable)
index 0000000..644a398
--- /dev/null
+++ b/tiload.h
@@ -0,0 +1,55 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** 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; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License along with
+** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+** Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** File:
+**     tiload.h
+**
+** Description:
+**     header file for tiload.c
+**
+** =============================================================================
+*/
+
+#ifndef _TILOAD_H
+#define _TILOAD_H
+
+#include "tas2557.h"
+
+#define BPR_REG(book, page, reg)               (((book * 256 * 128) + \
+                                                (page * 128)) + reg)
+
+/* typedefs required for the included header files */
+typedef char *string;
+
+typedef struct {
+       unsigned char nBook;
+       unsigned char nPage;
+       unsigned char nRegister;
+} BPR;
+
+/* defines */
+#define DEVICE_NAME     "tiload_node"
+#define TILOAD_IOC_MAGIC   0xE0
+#define TILOAD_IOMAGICNUM_GET                          _IOR(TILOAD_IOC_MAGIC, 1, int)
+#define TILOAD_IOMAGICNUM_SET                          _IOW(TILOAD_IOC_MAGIC, 2, int)
+#define TILOAD_BPR_READ                                _IOR(TILOAD_IOC_MAGIC, 3, BPR)
+#define TILOAD_BPR_WRITE                               _IOW(TILOAD_IOC_MAGIC, 4, BPR)
+#define TILOAD_IOCTL_SET_CONFIG                _IOW(TILOAD_IOC_MAGIC, 5, int)
+#define TILOAD_IOCTL_SET_CALIBRATION   _IOW(TILOAD_IOC_MAGIC, 6, int)
+
+int tiload_driver_init(struct tas2557_priv *pTAS2557);
+
+#endif