summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Selvaraj <joel.devbox@protonmail.com>2020-06-17 23:14:58 +0530
committerSumit Semwal <sumit.semwal@linaro.org>2021-01-08 12:28:53 +0530
commitd6f00660a05a119b106b9c383060e510dee85c17 (patch)
tree23f1f0460c48d12b4b2908e7bf4eacf866c0dab5
parentba245271fb8120b33a14ecf367a98e9160d30cdf (diff)
pmOS: beryllium: Add tas2559 audio amplifier codec
Initial code cherry-picked from https://gitlab.com/sdm845-mainline/sdm845-linux/-/commit/653fd6e9abef76c3b261527370cc18e06f4fc955 Also folded ("tas2559: fix compilation and enable i2c slave mode") https://gitlab.com/sdm845-mainline/sdm845-linux/-/commit/d20fd2046efbfec554040bda6d2daa3553172edd in this patch [AmitP: Updated calibration file path for AOSP, used sys_* for file operations instead of ksys_* and removed tiload and misc parts which makes no audible difference.] Signed-off-by: Amit Pundir <amit.pundir@linaro.org> Change-Id: I234d8af7e042e58138e231f9556d071f1d264561 Change-Id: I2be2763af9628be37583d4719a5d504de9b98a30 Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> [sumits: updated for v5.11-rc2]
-rw-r--r--sound/soc/codecs/Kconfig1
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/tas2559-codec.c1079
-rw-r--r--sound/soc/codecs/tas2559-codec.h30
-rw-r--r--sound/soc/codecs/tas2559-core.c3062
-rw-r--r--sound/soc/codecs/tas2559-core.h94
-rw-r--r--sound/soc/codecs/tas2559-regmap.c1188
-rw-r--r--sound/soc/codecs/tas2559.h523
-rw-r--r--sound/soc/codecs/tas2560.h142
9 files changed, 6123 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ba4eb54aafcb..a1e6d3b913c9 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -283,6 +283,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_WM9713
imply SND_SOC_WSA881X
imply SND_SOC_ZL38060
+ imply SND_SOC_TAS2559
help
Normally ASoC codec drivers are only built if a machine driver which
uses them is also built since they are only usable with a machine
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d277f0366e09..8aefd8fa1978 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -311,6 +311,10 @@ snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
snd-soc-tas2562-objs := tas2562.o
snd-soc-tas2764-objs := tas2764.o
+snd-soc-tas2559-objs := tas2559-core.o tas2559-regmap.o tas2559-codec.o
+ccflags-y += -DCONFIG_TAS2559_CODEC
+ccflags-y += -DDEBUG
+obj-$(CONFIG_SND_SOC_TAS2559) += snd-soc-tas2559.o
# Mux
snd-soc-simple-mux-objs := simple-mux.o
diff --git a/sound/soc/codecs/tas2559-codec.c b/sound/soc/codecs/tas2559-codec.c
new file mode 100644
index 000000000000..d138d2d9b52c
--- /dev/null
+++ b/sound/soc/codecs/tas2559-codec.c
@@ -0,0 +1,1079 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559-codec.c
+**
+** Description:
+** ALSA SoC driver for Texas Instruments TAS2559 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2559_CODEC
+
+#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/uaccess.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2559-core.h"
+#include "tas2559-codec.h"
+
+#define KCONTROL_CODEC
+
+static unsigned int tas2559_codec_read(struct snd_soc_component *pCodec,
+ unsigned int nRegister)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_err(pTAS2559->dev, "%s, ERROR, shouldn't be here\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_codec_write(struct snd_soc_component *pCodec, unsigned int nRegister,
+ unsigned int nValue)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_err(pTAS2559->dev, "%s, ERROR, shouldn't be here\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_codec_suspend(struct snd_soc_component *pCodec)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ pTAS2559->runtime_suspend(pTAS2559);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_codec_resume(struct snd_soc_component *pCodec)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ pTAS2559->runtime_resume(pTAS2559);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget tas2559_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 tas2559_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 tas2559_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static void tas2559_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+}
+
+static int tas2559_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ tas2559_enable(pTAS2559, !mute);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_set_dai_sysclk(struct snd_soc_dai *pDAI,
+ int nClkID, unsigned int nFreqency, int nDir)
+{
+ struct snd_soc_component *pCodec = pDAI->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s: freq = %u\n", __func__, nFreqency);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_hw_params(struct snd_pcm_substream *pSubstream,
+ struct snd_pcm_hw_params *pParams, struct snd_soc_dai *pDAI)
+{
+ struct snd_soc_component *pCodec = pDAI->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ /* do bit rate setting during platform data */
+ /* tas2559_set_bit_rate(pTAS2559, DevBoth, snd_pcm_format_width(params_format(pParams))); */
+ tas2559_set_sampling_rate(pTAS2559, params_rate(pParams));
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_set_dai_fmt(struct snd_soc_dai *pDAI, unsigned int nFormat)
+{
+ struct snd_soc_component *codec = pDAI->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_prepare(struct snd_pcm_substream *pSubstream,
+ struct snd_soc_dai *pDAI)
+{
+ struct snd_soc_component *codec = pDAI->component;
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_set_bias_level(struct snd_soc_component *pCodec,
+ enum snd_soc_bias_level eLevel)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+ dev_dbg(pTAS2559->dev, "%s: %d\n", __func__, eLevel);
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_codec_probe(struct snd_soc_component *pCodec)
+{
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(pCodec);
+
+ dev_err(pTAS2559->dev, "%s\n", __func__);
+ return 0;
+}
+
+static void tas2559_codec_remove(struct snd_soc_component *pCodec)
+{
+ //Downstream kernel - Do Nothing? Why even bother adding a function?
+}
+
+static int tas2559_power_ctrl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mbPowerUp;
+ dev_dbg(pTAS2559->dev, "%s = %d\n", __func__, pTAS2559->mbPowerUp);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_power_ctrl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ int nPowerOn = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s = %d\n", __func__, nPowerOn);
+ tas2559_enable(pTAS2559, (nPowerOn != 0));
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_fs_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int nFS = 48000;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ if (pTAS2559->mpFirmware->mnConfigurations)
+ nFS = pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration].mnSamplingRate;
+
+ pValue->value.integer.value[0] = nFS;
+ dev_dbg(pTAS2559->dev, "%s = %d\n", __func__, nFS);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_fs_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+ int nFS = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_info(pTAS2559->dev, "%s = %d\n", __func__, nFS);
+ ret = tas2559_set_sampling_rate(pTAS2559, nFS);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_DevA_Cali_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+ int prm_r0 = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_get_Cali_prm_r0(pTAS2559, DevA, &prm_r0);
+ pValue->value.integer.value[0] = prm_r0;
+ dev_dbg(pTAS2559->dev, "%s = 0x%x\n", __func__, prm_r0);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_DevB_Cali_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+ int prm_r0 = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_get_Cali_prm_r0(pTAS2559, DevB, &prm_r0);
+ pValue->value.integer.value[0] = prm_r0;
+ dev_dbg(pTAS2559->dev, "%s = 0x%x\n", __func__, prm_r0);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_program_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mnCurrentProgram;
+ dev_dbg(pTAS2559->dev, "%s = %d\n", __func__,
+ pTAS2559->mnCurrentProgram);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_program_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nProgram = pValue->value.integer.value[0];
+ int ret = 0, nConfiguration = -1;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ if (nProgram == pTAS2559->mnCurrentProgram)
+ nConfiguration = pTAS2559->mnCurrentConfiguration;
+
+ ret = tas2559_set_program(pTAS2559, nProgram, nConfiguration);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_configuration_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mnCurrentConfiguration;
+ dev_dbg(pTAS2559->dev, "%s = %d\n", __func__,
+ pTAS2559->mnCurrentConfiguration);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_configuration_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nConfiguration = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_info(pTAS2559->dev, "%s = %d\n", __func__, nConfiguration);
+ ret = tas2559_set_config(pTAS2559, nConfiguration);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_calibration_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mnCurrentCalibration;
+ dev_info(pTAS2559->dev, "%s = %d\n", __func__,
+ pTAS2559->mnCurrentCalibration);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_calibration_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nCalibration = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_set_calibration(pTAS2559, nCalibration);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_ldac_gain_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned char nGain = 0;
+ int ret = -1;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_get_DAC_gain(pTAS2559, DevA, &nGain);
+
+ if (ret >= 0)
+ pValue->value.integer.value[0] = nGain;
+
+ dev_dbg(pTAS2559->dev, "%s, ret = %d, %d\n", __func__, ret, nGain);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_ldac_gain_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nGain = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_set_DAC_gain(pTAS2559, DevA, nGain);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_rdac_gain_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned char nGain = 0;
+ int ret = -1;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_get_DAC_gain(pTAS2559, DevB, &nGain);
+
+ if (ret >= 0)
+ pValue->value.integer.value[0] = nGain;
+
+ dev_dbg(pTAS2559->dev, "%s, ret = %d, %d\n", __func__, ret, nGain);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return ret;
+}
+
+static int tas2559_rdac_gain_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nGain = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_set_DAC_gain(pTAS2559, DevB, nGain);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static const char * const dev_mute_text[] = {
+ "Mute",
+ "Unmute"
+};
+
+static const struct soc_enum dev_mute_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dev_mute_text), dev_mute_text),
+};
+
+static int tas2559_dev_a_mute_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ bool nMute = 0;
+ int ret = -1;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_DevMuteStatus(pTAS2559, DevA, &nMute);
+ if (ret >= 0)
+ pValue->value.integer.value[0] = nMute;
+ dev_dbg(pTAS2559->dev, "%s, ret = %d, %d\n", __func__, ret, nMute);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_dev_a_mute_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nMute = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_DevMute(pTAS2559, DevA, (nMute == 0));
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static int tas2559_dev_b_mute_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ bool nMute = 0;
+ int ret = -1;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_DevMuteStatus(pTAS2559, DevB, &nMute);
+ if (ret >= 0)
+ pValue->value.integer.value[0] = nMute;
+ dev_dbg(pTAS2559->dev, "%s, ret = %d, %d\n", __func__, ret, nMute);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return ret;
+}
+
+static int tas2559_dev_b_mute_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ unsigned int nMute = pValue->value.integer.value[0];
+ int ret = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ ret = tas2559_DevMute(pTAS2559, DevB, (nMute == 0));
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return ret;
+}
+
+static const char *const chl_setup_text[] = {
+ "default",
+ "DevA-Mute-DevB-Mute",
+ "DevA-Left-DevB-Right",
+ "DevA-Right-DevB-Left",
+ "DevA-MonoMix-DevB-MonoMix"
+};
+
+static const struct soc_enum chl_setup_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chl_setup_text), chl_setup_text),
+};
+
+static int tas2559_dsp_chl_setup_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mnChannelState;
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return 0;
+}
+
+static int tas2559_dsp_chl_setup_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int channel_state = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ tas2559_SA_DevChnSetup(pTAS2559, channel_state);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static const char * const vboost_ctl_text[] = {
+ "Default",
+ "AlwaysOn"
+};
+
+static const struct soc_enum vboost_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vboost_ctl_text), vboost_ctl_text),
+};
+
+static int tas2559_vboost_ctl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int nResult = 0, nVBoost = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ nResult = tas2559_get_VBoost(pTAS2559, &nVBoost);
+ if (nResult >= 0)
+ pValue->value.integer.value[0] = nVBoost;
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return 0;
+}
+
+static int tas2559_vboost_ctl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int vboost_state = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ tas2559_set_VBoost(pTAS2559, vboost_state, pTAS2559->mbPowerUp);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static const char * const vboost_volt_text[] = {
+ "Default",
+ "8.6V", /* (PPG 0dB) */
+ "8.1V", /* (PPG -1dB) */
+ "7.6V", /* (PPG -2dB) */
+ "6.6V", /* (PPG -3dB) */
+ "5.6V" /* (PPG -4dB) */
+};
+
+static const struct soc_enum vboost_volt_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vboost_volt_text), vboost_volt_text),
+};
+
+static int tas2559_vboost_volt_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int nVBstVolt = 0;
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ switch (pTAS2559->mnVBoostVoltage) {
+ case TAS2559_VBST_8P5V:
+ nVBstVolt = 1;
+ break;
+
+ case TAS2559_VBST_8P1V:
+ nVBstVolt = 2;
+ break;
+
+ case TAS2559_VBST_7P6V:
+ nVBstVolt = 3;
+ break;
+
+ case TAS2559_VBST_6P6V:
+ nVBstVolt = 4;
+ break;
+
+ case TAS2559_VBST_5P6V:
+ nVBstVolt = 5;
+ break;
+ }
+
+ pValue->value.integer.value[0] = nVBstVolt;
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return 0;
+}
+
+static int tas2559_vboost_volt_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int vbstvolt = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "%s, volt %d\n", __func__, vbstvolt);
+ tas2559_set_VBstVolt(pTAS2559, vbstvolt);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static const char *const echoref_ctl_text[] = {"DevA", "DevB", "DevBoth"};
+static const struct soc_enum echoref_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(echoref_ctl_text), echoref_ctl_text),
+};
+
+static int tas2559_echoref_ctl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mnEchoRef;
+
+ mutex_unlock(&pTAS2559->codec_lock);
+
+ return 0;
+}
+
+static int tas2559_echoref_ctl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+ int echoref = pValue->value.integer.value[0] & 0x01; /* only take care of left/right channel switch */
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ if (echoref != pTAS2559->mnEchoRef) {
+ pTAS2559->mnEchoRef = echoref;
+ tas2559_SA_ctl_echoRef(pTAS2559);
+ }
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ pValue->value.integer.value[0] = pTAS2559->mbMute;
+ dev_dbg(pTAS2559->dev, "tas2559_mute_ctrl_get = %d\n",
+ pTAS2559->mbMute);
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static int tas2559_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(pKcontrol);
+#else
+ struct snd_soc_component *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+ struct tas2559_priv *pTAS2559 = snd_soc_component_get_drvdata(codec);
+
+ int mbMute = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2559->codec_lock);
+
+ dev_dbg(pTAS2559->dev, "tas2559_mute_ctrl_put = %d\n", mbMute);
+
+ pTAS2559->mbMute = !!mbMute;
+
+ mutex_unlock(&pTAS2559->codec_lock);
+ return 0;
+}
+
+static const char *const vendor_id_text[] = {"None", "AAC", "SSI", "GOER", "Unknown"};
+static const struct soc_enum vendor_id[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vendor_id_text), vendor_id_text),
+};
+
+static int vendor_id_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ (void)kcontrol;
+ ucontrol->value.integer.value[0] = 1;
+ return 0;
+}
+
+static const struct snd_kcontrol_new tas2559_snd_controls[] = {
+ SOC_SINGLE_EXT("TAS2559 DAC Playback Volume", SND_SOC_NOPM, 0, 0x0f, 0,
+ tas2559_ldac_gain_get, tas2559_ldac_gain_put),
+ SOC_SINGLE_EXT("TAS2560 DAC Playback Volume", SND_SOC_NOPM, 0, 0x0f, 0,
+ tas2559_rdac_gain_get, tas2559_rdac_gain_put),
+ SOC_SINGLE_EXT("PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas2559_power_ctrl_get, tas2559_power_ctrl_put),
+ SOC_SINGLE_EXT("Program", SND_SOC_NOPM, 0, 0x00FF, 0,
+ tas2559_program_get, tas2559_program_put),
+ SOC_SINGLE_EXT("Configuration", SND_SOC_NOPM, 0, 0x00FF, 0,
+ tas2559_configuration_get, tas2559_configuration_put),
+ SOC_SINGLE_EXT("FS", SND_SOC_NOPM, 8000, 48000, 0,
+ tas2559_fs_get, tas2559_fs_put),
+ SOC_SINGLE_EXT("Get DevA Cali_Re", SND_SOC_NOPM, 0, 0x7f000000, 0,
+ tas2559_DevA_Cali_get, NULL),
+ SOC_SINGLE_EXT("Get DevB Cali_Re", SND_SOC_NOPM, 0, 0x7f000000, 0,
+ tas2559_DevB_Cali_get, NULL),
+ SOC_SINGLE_EXT("Calibration", SND_SOC_NOPM, 0, 0x00FF, 0,
+ tas2559_calibration_get, tas2559_calibration_put),
+ SOC_ENUM_EXT("Stereo DSPChl Setup", chl_setup_enum[0],
+ tas2559_dsp_chl_setup_get, tas2559_dsp_chl_setup_put),
+ SOC_ENUM_EXT("VBoost Ctrl", vboost_ctl_enum[0],
+ tas2559_vboost_ctl_get, tas2559_vboost_ctl_put),
+ SOC_ENUM_EXT("VBoost Volt", vboost_volt_enum[0],
+ tas2559_vboost_volt_get, tas2559_vboost_volt_put),
+ SOC_ENUM_EXT("Stereo EchoRef Ctrl", echoref_ctl_enum[0],
+ tas2559_echoref_ctl_get, tas2559_echoref_ctl_put),
+ SOC_ENUM_EXT("TAS2559 Mute", dev_mute_enum[0],
+ tas2559_dev_a_mute_get, tas2559_dev_a_mute_put),
+ SOC_ENUM_EXT("TAS2560 Mute", dev_mute_enum[0],
+ tas2559_dev_b_mute_get, tas2559_dev_b_mute_put),
+ SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas2559_mute_ctrl_get, tas2559_mute_ctrl_put),
+ SOC_ENUM_EXT("SPK ID", vendor_id, vendor_id_get, NULL),
+};
+
+static const struct snd_soc_component_driver soc_codec_driver_tas2559 = {
+ .probe = tas2559_codec_probe,
+ .remove = tas2559_codec_remove,
+ .read = tas2559_codec_read,
+ .write = tas2559_codec_write,
+ .suspend = tas2559_codec_suspend,
+ .resume = tas2559_codec_resume,
+ .set_bias_level = tas2559_set_bias_level,
+ .idle_bias_on = false,
+ .controls = tas2559_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2559_snd_controls),
+ .dapm_widgets = tas2559_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2559_dapm_widgets),
+ .dapm_routes = tas2559_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2559_audio_map),
+};
+
+static struct snd_soc_dai_ops tas2559_dai_ops = {
+ .startup = tas2559_startup,
+ .shutdown = tas2559_shutdown,
+ .mute_stream = tas2559_mute,
+ .hw_params = tas2559_hw_params,
+ .prepare = tas2559_prepare,
+ .set_sysclk = tas2559_set_dai_sysclk,
+ .set_fmt = tas2559_set_dai_fmt,
+};
+
+#define TAS2559_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 tas2559_dai_driver[] = {
+ {
+ .name = "tas2559 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2559_FORMATS,
+ },
+ .ops = &tas2559_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "tas2559 ASI2",
+ .id = 1,
+ .playback = {
+ .stream_name = "ASI2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2559_FORMATS,
+ },
+ .ops = &tas2559_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "tas2559 ASIM",
+ .id = 2,
+ .playback = {
+ .stream_name = "ASIM Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2559_FORMATS,
+ },
+ .ops = &tas2559_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+int tas2559_register_codec(struct tas2559_priv *pTAS2559)
+{
+ int nResult = 0;
+
+ dev_info(pTAS2559->dev, "%s, enter\n", __func__);
+ nResult = devm_snd_soc_register_component(pTAS2559->dev,
+ &soc_codec_driver_tas2559,
+ tas2559_dai_driver, ARRAY_SIZE(tas2559_dai_driver));
+ return nResult;
+}
+
+int tas2559_deregister_codec(struct tas2559_priv *pTAS2559)
+{
+ snd_soc_unregister_component(pTAS2559->dev);
+ return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2559 ALSA SOC Smart Amplifier Stereo driver");
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/sound/soc/codecs/tas2559-codec.h b/sound/soc/codecs/tas2559-codec.h
new file mode 100644
index 000000000000..17093a5f73e4
--- /dev/null
+++ b/sound/soc/codecs/tas2559-codec.h
@@ -0,0 +1,30 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559-codec.h
+**
+** Description:
+** header file for tas2559-codec.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2559_CODEC_H
+#define _TAS2559_CODEC_H
+
+#include "tas2559.h"
+
+int tas2559_register_codec(struct tas2559_priv *pTAS2559);
+int tas2559_deregister_codec(struct tas2559_priv *pTAS2559);
+
+#endif /* _TAS2559_CODEC_H */
diff --git a/sound/soc/codecs/tas2559-core.c b/sound/soc/codecs/tas2559-core.c
new file mode 100644
index 000000000000..afabb17860a8
--- /dev/null
+++ b/sound/soc/codecs/tas2559-core.c
@@ -0,0 +1,3062 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559-core.c
+**
+** Description:
+** TAS2559 common functions for Android Linux
+**
+** =============================================================================
+*/
+
+#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/uaccess.h>
+#include <linux/crc8.h>
+
+#include "tas2560.h"
+#include "tas2559-core.h"
+
+#define TAS2559_CAL_NAME "/vendor/firmware/qcom/sdm845/tas2559_cal.bin"
+#define RESTART_MAX 3
+
+static int tas2559_load_calibration(struct tas2559_priv *pTAS2559,
+ char *pFileName);
+static int tas2559_load_data(struct tas2559_priv *pTAS2559, struct TData *pData,
+ unsigned int nType);
+static void tas2559_clear_firmware(struct TFirmware *pFirmware);
+static int tas2559_load_block(struct tas2559_priv *pTAS2559, struct TBlock *pBlock);
+static int tas2559_load_configuration(struct tas2559_priv *pTAS2559,
+ unsigned int nConfiguration, bool bLoadSame);
+
+#define TAS2559_UDELAY 0xFFFFFFFE
+#define TAS2559_MDELAY 0xFFFFFFFD
+
+#define FW_ERR_HEADER -1
+#define FW_ERR_SIZE -2
+
+#define TAS2559_BLOCK_PLL 0x00
+#define TAS2559_BLOCK_PGM_ALL 0x0d
+#define TAS2559_BLOCK_PGM_DEV_A 0x01
+#define TAS2559_BLOCK_PGM_DEV_B 0x08
+#define TAS2559_BLOCK_CFG_COEFF_DEV_A 0x03
+#define TAS2559_BLOCK_CFG_COEFF_DEV_B 0x0a
+#define TAS2559_BLOCK_CFG_PRE_DEV_A 0x04
+#define TAS2559_BLOCK_CFG_PRE_DEV_B 0x0b
+#define TAS2559_BLOCK_CFG_POST 0x05
+#define TAS2559_BLOCK_CFG_POST_POWER 0x06
+#define TAS2559_BLOCK_PST_POWERUP_DEV_B 0x0e
+
+#define PPC_DRIVER_CRCCHK 0x00000200
+#define PPC_DRIVER_CONFDEV 0x00000300
+#define PPC_DRIVER_MTPLLSRC 0x00000400
+#define PPC_DRIVER_CFGDEV_NONCRC 0x00000101
+
+static unsigned int p_tas2559_default_data[] = {
+ DevA, TAS2559_SAR_ADC2_REG, 0x05,/* enable SAR ADC */
+ DevA, TAS2559_CLK_ERR_CTRL2, 0x21,/*clk1:clock hysteresis, 0.34ms; clock halt, 22ms*/
+ DevA, TAS2559_CLK_ERR_CTRL3, 0x21,/*clk2: rampDown 15dB/us, clock hysteresis, 10.66us; clock halt, 22ms */
+ DevB, TAS2560_CLK_ERR_CTRL2, 0x21,/*rampDown 15dB/us, clock1 hysteresis, 0.34ms; clock2 hysteresis, 10.6us */
+ DevA, TAS2559_SAFE_GUARD_REG, TAS2559_SAFE_GUARD_PATTERN,/* safe guard */
+ DevA, TAS2559_CLK_ERR_CTRL, 0x00,/*enable clock error detection*/
+ DevB, TAS2560_CLK_ERR_CTRL, 0x00,/* disable clock error detection */
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_irq_config[] = {
+ DevA, TAS2559_CLK_HALT_REG, 0x71,/* enable clk halt detect2 interrupt */
+ DevA, TAS2559_INT_GEN1_REG, 0x11,/* enable spk OC and OV*/
+ DevA, TAS2559_INT_GEN2_REG, 0x11,/* enable clk err1 and die OT*/
+ DevA, TAS2559_INT_GEN3_REG, 0x11,/* enable clk err2 and brownout*/
+ DevA, TAS2559_INT_GEN4_REG, 0x01,/* disable SAR, enable clk halt*/
+ DevB, TAS2560_INT_GEN_REG, 0xff,/* enable spk OC and OV*/
+ DevA, TAS2559_GPIO4_PIN_REG, 0x07,/* set GPIO4 as int1, default*/
+ DevB, TAS2560_IRQ_PIN_REG, 0x41,
+ DevA, TAS2559_INT_MODE_REG, 0x80,/* active high until INT_STICKY_1 and INT_STICKY_2 are read to be cleared. */
+ DevB, TAS2560_INT_MODE_REG, 0x80,/* active high until INT_STICKY_1 and INT_STICKY_2 are read to be cleared. */
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_startup_data[] = {
+ DevA, TAS2559_GPIO1_PIN_REG, 0x01,/* enable BCLK */
+ DevA, TAS2559_GPIO2_PIN_REG, 0x01,/* enable WCLK */
+ DevA, TAS2559_POWER_CTRL2_REG, 0xA0,/*Class-D, Boost power up*/
+ DevA, TAS2559_POWER_CTRL2_REG, 0xA3,/*Class-D, Boost, IV sense power up*/
+ DevA, TAS2559_POWER_CTRL1_REG, 0xF8,/*PLL, DSP, clock dividers power up*/
+ DevBoth, TAS2559_UDELAY, 2000,/*delay*/
+ DevB, TAS2560_DEV_MODE_REG, 0x02,
+ DevB, TAS2560_MUTE_REG, 0x41,
+ DevBoth, TAS2559_UDELAY, 2000,/*delay*/
+ DevA, TAS2559_CLK_ERR_CTRL, 0x2B,/*enable clock error detection*/
+ DevB, TAS2560_CLK_ERR_CTRL, 0x0B,/* disable clock error detection */
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_mute_data[] = {
+ DevA, TAS2559_SOFT_MUTE_REG, 0x01,/*soft mute*/
+ DevB, TAS2560_MUTE_REG, 0x41,
+ DevA, TAS2559_MDELAY, 10,/*delay 10ms*/
+ DevA, TAS2559_MUTE_REG, 0x03,/*mute*/
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_unmute_data[] = {
+ DevA, TAS2559_MUTE_REG, 0x00, /*unmute*/
+ DevB, TAS2560_MUTE_REG, 0x40,
+ DevA, TAS2559_SOFT_MUTE_REG, 0x00, /*soft unmute*/
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_shutdown_data[] = {
+ DevA, TAS2559_CLK_ERR_CTRL, 0x00,/* disable clock error detection */
+ DevB, TAS2560_CLK_ERR_CTRL, 0x00,/* disable clock error detection */
+ DevA, TAS2559_SOFT_MUTE_REG, 0x01,/*soft mute*/
+ DevB, TAS2560_MUTE_REG, 0x41,
+ DevB, TAS2560_MUTE_REG, 0x01,
+ DevBoth, TAS2559_MDELAY, 10,/*delay 10ms*/
+ DevB, TAS2559_MDELAY, 20,/*delay 20ms*/
+ DevA, TAS2559_POWER_CTRL1_REG, 0x60,/*DSP power down*/
+ DevA, TAS2559_MDELAY, 2,/*delay 20ms*/
+ DevA, TAS2559_MUTE_REG, 0x03,/*mute*/
+ DevA, TAS2559_POWER_CTRL2_REG, 0x00,/*Class-D, Boost power down*/
+ DevA, TAS2559_POWER_CTRL1_REG, 0x00,/*all power down*/
+ DevB, TAS2560_DEV_MODE_REG, 0x01,
+ DevA, TAS2559_GPIO1_PIN_REG, 0x00,/* disable BCLK */
+ DevA, TAS2559_GPIO2_PIN_REG, 0x00,/* disable WCLK */
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2559_shutdown_DevB_data[] = {
+ DevA, TAS2559_CLK_ERR_CTRL, 0x00,/* disable clock error detection */
+ DevB, TAS2560_CLK_ERR_CTRL, 0x00,/* disable clock error detection */
+ DevB, TAS2560_MUTE_REG, 0x41,
+ DevB, TAS2560_MUTE_REG, 0x01,
+ DevA, TAS2559_POWER_CTRL1_REG, 0x60,/*DSP power down*/
+ DevBoth, TAS2559_MDELAY, 30,/*delay 2ms*/
+ DevB, TAS2560_DEV_MODE_REG, 0x01,
+ DevA, TAS2559_POWER_CTRL2_REG, 0x00,/*Class-D, Boost power down*/
+ DevA, TAS2559_POWER_CTRL1_REG, 0x00,/*all power down*/
+ DevA, TAS2559_GPIO1_PIN_REG, 0x00,/* disable BCLK */
+ DevA, TAS2559_GPIO2_PIN_REG, 0x00,/* disable WCLK */
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static int tas2559_dev_load_data(struct tas2559_priv *pTAS2559,
+ enum channel dev, unsigned int *pData)
+{
+ int nResult = 0;
+ unsigned int n = 0;
+ unsigned int nRegister;
+ unsigned int nData;
+ enum channel chl;
+
+ do {
+ chl = pData[n * 3];
+
+ if (chl == 0xffffffff)
+ break;
+
+ if (dev & chl) {
+ nRegister = pData[n * 3 + 1];
+ nData = pData[n * 3 + 2];
+
+ if (nRegister == TAS2559_UDELAY) {
+ udelay(nData);
+ dev_dbg(pTAS2559->dev, "%s, udelay %d\n", __func__, nData);
+ } else if (nRegister == TAS2559_MDELAY) {
+ mdelay(nData);
+ dev_dbg(pTAS2559->dev, "%s, msleep %d\n", __func__, nData);
+ } else if (nRegister != 0xFFFFFFFF) {
+ dev_dbg(pTAS2559->dev, "%s, write chl=%d, B[%d]P[%d]R[%d]=0x%x\n",
+ __func__, chl, TAS2559_BOOK_ID(nRegister),
+ TAS2559_PAGE_ID(nRegister), TAS2559_PAGE_REG(nRegister), nData);
+ nResult = pTAS2559->write(pTAS2559, chl, nRegister, nData);
+ if (nResult < 0)
+ break;
+ }
+ }
+
+ n++;
+ } while (nRegister != 0xFFFFFFFF);
+
+ return nResult;
+}
+
+/* reserved
+*static int tas2559_dev_load_blk_data(struct tas2559_priv *pTAS2559,
+* enum channel chl, unsigned int *pData)
+*{
+* unsigned int nRegister;
+* unsigned int *nData;
+* unsigned char Buf[128];
+* unsigned int nLength = 0;
+* unsigned int i =0;
+* unsigned int nSize = 0;
+* int nResult = 0;
+*
+* do {
+* nRegister = pData[nLength];
+* nSize = pData[nLength + 1];
+* nData = &pData[nLength + 2];
+* if (nRegister == TAS2559_MDELAY) {
+* mdelay(nData[0]);
+* dev_dbg(pTAS2559->dev, "%s, mDelay %d\n", __func__, nData[0]);
+* } else if (nRegister == 0xFFFFFFFF) {
+* dev_dbg(pTAS2559->dev, "%s, end\n", __func__);
+* break;
+* } else if (nSize > 128) {
+* dev_err(pTAS2559->dev, "%s, maximum is 128 bytes!\n",
+* __func__);
+* break;
+* } else if (nSize == 0) {
+* dev_err(pTAS2559->dev, "%s, minimum is 1 bytes!\n",
+* __func__);
+* break;
+* } else {
+* if (nSize > 1) {
+* for(i = 0; i < nSize; i++)
+* Buf[i] = (unsigned char)nData[i];
+* dev_dbg(pTAS2559->dev, "%s, BW B[%d]P[%d]R[%d], size=%d, D0=0x%x\n",
+* __func__, TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister),
+* TAS2559_PAGE_REG(nRegister), nSize, Buf[0]);
+* nResult = pTAS2559->bulk_write(pTAS2559, chl, nRegister, Buf, nSize);
+* } else {
+* dev_dbg(pTAS2559->dev, "%s, W B[%d]P[%d]R[%d], Data=0x%x\n",
+* __func__, TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister),
+* TAS2559_PAGE_REG(nRegister), nData[0]);
+* nResult = pTAS2559->write(pTAS2559,chl,nRegister, nData[0]);
+* }
+* if (nResult < 0)
+* break;
+* }
+* nLength = nLength + 2 + pData[nLength+1] ;
+* } while (nRegister != 0xFFFFFFFF);
+*
+* return nResult;
+*}
+*/
+
+static int tas2559_DevStartup(struct tas2559_priv *pTAS2559,
+ unsigned int dev)
+{
+ int nResult = 0;
+ enum channel chl = dev;
+
+ if (dev == DevB)
+ chl = DevBoth;
+
+ dev_dbg(pTAS2559->dev, "%s, chl=%d\n", __func__, chl);
+ nResult = tas2559_dev_load_data(pTAS2559, chl, p_tas2559_startup_data);
+
+ return nResult;
+}
+
+static int tas2559_DevShutdown(struct tas2559_priv *pTAS2559,
+ unsigned int dev)
+{
+ int nResult = 0;
+
+ dev_dbg(pTAS2559->dev, "%s, dev=%d\n", __func__, dev);
+
+ if (dev == DevB)
+ nResult = tas2559_dev_load_data(pTAS2559, dev, p_tas2559_shutdown_DevB_data);
+ else
+ nResult = tas2559_dev_load_data(pTAS2559, dev, p_tas2559_shutdown_data);
+
+ return nResult;
+}
+
+int tas2559_configIRQ(struct tas2559_priv *pTAS2559, enum channel dev)
+{
+ return tas2559_dev_load_data(pTAS2559, dev, p_tas2559_irq_config);
+}
+
+int tas2559_SA_DevChnSetup(struct tas2559_priv *pTAS2559, unsigned int mode)
+{
+ int nResult = 0;
+ struct TProgram *pProgram;
+ unsigned char buf_mute[16] = {0};
+ unsigned char buf_DevA_Left_DevB_Right[16] = {0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0, 0, 0};
+ unsigned char buf_DevA_Right_DevB_Left[16] = {0, 0, 0, 0, 0x40, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char buf_DevA_MonoMix_DevB_MonoMix[16] = {0x20, 0, 0, 0, 0x20, 0, 0, 0, 0x20, 0, 0, 0, 0x20, 0, 0, 0};
+ unsigned char *pDevBuf = NULL;
+
+ dev_dbg(pTAS2559->dev, "%s, mode %d\n", __func__, mode);
+ if ((pTAS2559->mpFirmware->mnPrograms == 0)
+ || (pTAS2559->mpFirmware->mnConfigurations == 0)) {
+ dev_err(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+ if (pProgram->mnAppMode != TAS2559_APP_TUNINGMODE) {
+ dev_err(pTAS2559->dev, "%s, not tuning mode\n", __func__);
+ goto end;
+ }
+
+ if (pTAS2559->mbLoadConfigurationPrePowerUp) {
+ dev_dbg(pTAS2559->dev, "%s, setup channel after coeff update\n", __func__);
+ pTAS2559->mnChannelState = mode;
+ goto end;
+ }
+
+ switch (mode) {
+ case TAS2559_AD_BD:
+ pDevBuf = pTAS2559->mnDefaultChlData;
+ break;
+
+ case TAS2559_AM_BM:
+ pDevBuf = buf_mute;
+ break;
+
+ case TAS2559_AL_BR:
+ pDevBuf = buf_DevA_Left_DevB_Right;
+ break;
+
+ case TAS2559_AR_BL:
+ pDevBuf = buf_DevA_Right_DevB_Left;
+ break;
+
+ case TAS2559_AH_BH:
+ pDevBuf = buf_DevA_MonoMix_DevB_MonoMix;
+ break;
+
+ default:
+ goto end;
+ }
+
+ if (pDevBuf) {
+ nResult = pTAS2559->bulk_write(pTAS2559, DevA,
+ TAS2559_SA_CHL_CTRL_REG, pDevBuf, 16);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnChannelState = mode;
+ }
+
+end:
+ return nResult;
+}
+
+int tas2559_SA_ctl_echoRef(struct tas2559_priv *pTAS2559)
+{
+ int nResult = 0;
+
+ /*
+ * by default:
+ * TAS2559 echo-ref is on DOUT left channel,
+ * TAS2560 echo-ref is on DOUT right channel
+ */
+ return nResult;
+}
+
+int tas2559_set_DAC_gain(struct tas2559_priv *pTAS2559,
+ enum channel chl, unsigned int nGain)
+{
+ int nResult = 0;
+ int gain = (nGain & 0x0f);
+
+ dev_dbg(pTAS2559->dev, "%s, nGain: %d", __func__, nGain);
+
+ if (chl & DevA) {
+ nResult = pTAS2559->update_bits(pTAS2559, DevA,
+ TAS2559_SPK_CTRL_REG, 0x78, (gain << 3));
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (chl & DevB)
+ nResult = pTAS2559->update_bits(pTAS2559, DevB,
+ TAS2560_SPK_CTRL_REG, 0x0f, gain);
+
+end:
+
+ return nResult;
+}
+
+int tas2559_get_DAC_gain(struct tas2559_priv *pTAS2559,
+ enum channel chl, unsigned char *pnGain)
+{
+ int nResult = 0;
+ int nGain;
+
+ if (chl == DevA) {
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_SPK_CTRL_REG, &nGain);
+
+ if (nResult >= 0)
+ *pnGain = ((nGain >> 3) & 0x0f);
+ } else
+ if (chl == DevB) {
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_SPK_CTRL_REG, &nGain);
+
+ if (nResult >= 0)
+ *pnGain = (nGain & 0x0f);
+ }
+
+ return nResult;
+}
+
+int tas2559_set_bit_rate(struct tas2559_priv *pTAS2559, unsigned int nBitRate)
+{
+ int nResult = 0, n = -1;
+
+ dev_dbg(pTAS2559->dev, "%s: nBitRate = %d\n", __func__, 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) {
+ nResult = pTAS2559->update_bits(pTAS2559, DevA,
+ TAS2559_ASI1_DAC_FORMAT_REG, 0x18, n << 3);
+ if (nResult >= 0) {
+ /* The ASIM is always configured for 16-bits, hardcode the TAS2560 to 16-bits */
+ nResult = pTAS2559->update_bits(pTAS2559, DevB,
+ TAS2560_DAI_FMT, 0x03, 0);
+ }
+ }
+
+ return nResult;
+}
+
+int tas2559_get_bit_rate(struct tas2559_priv *pTAS2559, unsigned char *pBitRate)
+{
+ int nResult = 0;
+ unsigned int nValue = 0;
+ unsigned char bitRate;
+
+ nResult = pTAS2559->read(pTAS2559, DevA,
+ TAS2559_ASI1_DAC_FORMAT_REG, &nValue);
+
+ if (nResult >= 0) {
+ bitRate = (nValue & 0x18) >> 3;
+
+ if (bitRate == 0)
+ bitRate = 16;
+ else
+ if (bitRate == 1)
+ bitRate = 20;
+ else
+ if (bitRate == 2)
+ bitRate = 24;
+ else
+ bitRate = 32;
+
+ *pBitRate = bitRate;
+ }
+
+ return nResult;
+}
+
+int tas2559_DevMute(struct tas2559_priv *pTAS2559, enum channel dev, bool mute)
+{
+ int nResult = 0;
+
+ dev_dbg(pTAS2559->dev, "%s, dev=%d, mute=%d\n", __func__, dev, mute);
+
+ if (pTAS2559->mbMute) {
+ dev_dbg(pTAS2559->dev, "%s, always mute \n", __func__);
+ return tas2559_dev_load_data(pTAS2559, dev, p_tas2559_mute_data);
+ }
+
+ if (mute)
+ nResult = tas2559_dev_load_data(pTAS2559, dev, p_tas2559_mute_data);
+ else
+ nResult = tas2559_dev_load_data(pTAS2559, dev, p_tas2559_unmute_data);
+
+ return nResult;
+}
+
+int tas2559_DevMuteStatus(struct tas2559_priv *pTAS2559, enum channel dev, bool *pMute)
+{
+ int nResult = 0;
+ int nMute = 0;
+
+ if (dev == DevA)
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_SOFT_MUTE_REG, &nMute);
+ else if (dev == DevB)
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_MUTE_REG, &nMute);
+ else
+ goto end;
+
+ *pMute = ((nMute & 0x01) == 0x00);
+
+end:
+ return nResult;
+}
+
+/*
+* die temperature calculation:
+* DieTemp = readout / 2^23
+*/
+int tas2559_get_die_temperature(struct tas2559_priv *pTAS2559, int *pTemperature)
+{
+ int nResult = 0;
+ unsigned char nBuf[4];
+ int temp;
+
+ if (!pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ if (!pTAS2559->mbPowerUp) {
+ dev_err(pTAS2559->dev, "%s, device not powered on\n", __func__);
+ goto end;
+ }
+
+ /* TAS2559 should always be enabled */
+ nResult = pTAS2559->bulk_read(pTAS2559, DevA, TAS2559_DIE_TEMP_REG, nBuf, 4);
+
+ if (nResult >= 0) {
+ temp = ((int)nBuf[0] << 24) | ((int)nBuf[1] << 16) | ((int)nBuf[2] << 8) | nBuf[3];
+ *pTemperature = temp;
+ }
+
+end:
+
+ return nResult;
+}
+
+int tas2559_set_VBstVolt(struct tas2559_priv *pTAS2559, unsigned int vbstvolt)
+{
+ int nResult = 0;
+
+ if (pTAS2559->mbPowerUp)
+ goto end;
+
+ switch (vbstvolt) {
+ case 1:
+ pTAS2559->mnVBoostVoltage = TAS2559_VBST_8P5V;
+ break;
+
+ case 2:
+ pTAS2559->mnVBoostVoltage = TAS2559_VBST_8P1V;
+ break;
+
+ case 3:
+ pTAS2559->mnVBoostVoltage = TAS2559_VBST_7P6V;
+ break;
+
+ case 4:
+ pTAS2559->mnVBoostVoltage = TAS2559_VBST_6P6V;
+ break;
+
+ case 5:
+ pTAS2559->mnVBoostVoltage = TAS2559_VBST_5P6V;
+ break;
+ }
+
+ pTAS2559->mbLoadVBoostPrePowerUp = true;
+end:
+
+ return nResult;
+}
+
+int tas2559_update_VBstVolt(struct tas2559_priv *pTAS2559, enum channel chn)
+{
+ int nResult = 0;
+ int nVBstVoltSet = -1;
+
+ switch (pTAS2559->mnVBoostVoltage) {
+ case TAS2559_VBST_8P5V:
+ nVBstVoltSet = 6;
+ dev_warn(pTAS2559->dev, "%s, PPG of this snapshot should be 0dB\n", __func__);
+ break;
+
+ case TAS2559_VBST_8P1V:
+ nVBstVoltSet = 5;
+ dev_warn(pTAS2559->dev, "%s, PPG of this snapshot should be -1dB\n", __func__);
+ break;
+
+ case TAS2559_VBST_7P6V:
+ nVBstVoltSet = 4;
+ dev_warn(pTAS2559->dev, "%s, PPG of this snapshot should be -2dB\n", __func__);
+ break;
+
+ case TAS2559_VBST_6P6V:
+ nVBstVoltSet = 2;
+ dev_warn(pTAS2559->dev, "%s, PPG of this snapshot should be -3dB\n", __func__);
+ break;
+
+ case TAS2559_VBST_5P6V:
+ nVBstVoltSet = 0;
+ dev_warn(pTAS2559->dev, "%s, PPG of this snapshot should be -4dB\n", __func__);
+ break;
+
+ default:
+ dev_err(pTAS2559->dev, "%s, error volt %d\n", __func__, pTAS2559->mnVBoostVoltage);
+ break;
+ }
+
+ if (nVBstVoltSet >= 0) {
+ if (chn & DevA)
+ nResult = pTAS2559->update_bits(pTAS2559, DevA, TAS2559_VBST_VOLT_REG, 0xe0, (nVBstVoltSet << 5));
+ if (chn & DevB) {
+ nResult = pTAS2559->update_bits(pTAS2559, DevB, TAS2560_VBST_VOLT_REG, 0xe0, (nVBstVoltSet << 5));
+ }
+ dev_dbg(pTAS2559->dev, "%s, set vbst voltage (%d channel) 0x%x\n", __func__, chn, (nVBstVoltSet << 5));
+ }
+
+ return nResult;
+}
+
+int tas2559_get_VBoost(struct tas2559_priv *pTAS2559, int *pVBoost)
+{
+ int nResult = 0;
+
+ dev_dbg(pTAS2559->dev, "%s, VBoost state %d\n", __func__, pTAS2559->mnVBoostState);
+ switch (pTAS2559->mnVBoostState) {
+ case TAS2559_VBST_NEED_DEFAULT:
+ case TAS2559_VBST_DEFAULT:
+ *pVBoost = 0;
+ break;
+
+ case TAS2559_VBST_A_ON:
+ case TAS2559_VBST_B_ON:
+ case TAS2559_VBST_A_ON_B_ON:
+ *pVBoost = 1;
+ break;
+ default:
+ dev_err(pTAS2559->dev, "%s, error state %d\n", __func__, pTAS2559->mnVBoostState);
+ break;
+ }
+
+ return nResult;
+}
+
+static int tas2559_restore_VBstCtl(struct tas2559_priv *pTAS2559, enum channel chn)
+{
+ int nResult = 0;
+ unsigned int nDevAVBstCtrl, nDevASlpCtrl, nDevABstLevel;
+ unsigned int nDevBVBstCtrl, nDevBSlpCtrl, nDevBBstLevel;
+
+ if (chn & DevA) {
+ nDevAVBstCtrl = pTAS2559->mnVBoostDefaultCfg[0];
+ nDevASlpCtrl = pTAS2559->mnVBoostDefaultCfg[1];
+ nDevABstLevel = pTAS2559->mnVBoostDefaultCfg[2];
+ nResult = pTAS2559->write(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, nDevAVBstCtrl);
+ if (nResult < 0)
+ goto DevB;
+ nResult = pTAS2559->write(pTAS2559, DevA, TAS2559_SLEEPMODE_CTL_REG, nDevASlpCtrl);
+ if (nResult < 0)
+ goto DevB;
+ nResult = pTAS2559->write(pTAS2559, DevA, TAS2559_VBST_VOLT_REG, nDevABstLevel);
+ if (nResult < 0)
+ goto DevB;
+ }
+
+DevB:
+ if (chn & DevB) {
+ nDevBVBstCtrl = pTAS2559->mnVBoostDefaultCfg[3];
+ nDevBSlpCtrl = pTAS2559->mnVBoostDefaultCfg[4];
+ nDevBBstLevel = pTAS2559->mnVBoostDefaultCfg[5];
+ nResult = pTAS2559->write(pTAS2559, DevB, TAS2560_VBOOST_CTL_REG, nDevBVBstCtrl);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->write(pTAS2559, DevB, TAS2560_SLEEPMODE_CTL_REG, nDevBSlpCtrl);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->write(pTAS2559, DevB, TAS2560_VBST_VOLT_REG, nDevBBstLevel);
+ if (nResult < 0)
+ goto end;
+ }
+end:
+ return nResult;
+}
+
+int tas2559_set_VBoost(struct tas2559_priv *pTAS2559, int vboost, bool bPowerOn)
+{
+ int nResult = 0;
+ struct TConfiguration *pConfiguration;
+ unsigned int nConfig;
+
+ dev_dbg(pTAS2559->dev, "%s", __func__);
+
+ if ((!pTAS2559->mpFirmware->mnConfigurations)
+ || (!pTAS2559->mpFirmware->mnPrograms)) {
+ dev_err(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ pTAS2559->mbLoadVBoostPrePowerUp = true;
+ pTAS2559->mnVBoostNewState = vboost;
+
+ if (bPowerOn) {
+ dev_info(pTAS2559->dev, "%s, will load VBoost state next time before power on\n", __func__);
+ pTAS2559->mbLoadVBoostPrePowerUp = true;
+ pTAS2559->mnVBoostNewState = vboost;
+ goto end;
+ }
+
+ if (pTAS2559->mbLoadConfigurationPrePowerUp)
+ nConfig = pTAS2559->mnNewConfiguration;
+ else
+ nConfig = pTAS2559->mnCurrentConfiguration;
+
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[nConfig]);
+
+ dev_dbg(pTAS2559->dev, "VBoost state: %d, nConfig: %d", pTAS2559->mnVBoostState, nConfig);
+
+ if (pTAS2559->mnVBoostState == TAS2559_VBST_NEED_DEFAULT) {
+ if (pConfiguration->mnDevices & DevA) {
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, &pTAS2559->mnVBoostDefaultCfg[0]);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_SLEEPMODE_CTL_REG, &pTAS2559->mnVBoostDefaultCfg[1]);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_VBST_VOLT_REG, &pTAS2559->mnVBoostDefaultCfg[2]);
+ if (nResult < 0)
+ goto end;
+ }
+ if (pConfiguration->mnDevices & DevB) {
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_VBOOST_CTL_REG, &pTAS2559->mnVBoostDefaultCfg[3]);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_SLEEPMODE_CTL_REG, &pTAS2559->mnVBoostDefaultCfg[4]);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_VBST_VOLT_REG, &pTAS2559->mnVBoostDefaultCfg[5]);
+ if (nResult < 0)
+ goto end;
+ }
+ dev_dbg(pTAS2559->dev, "%s, get default VBoost\n", __func__);
+ pTAS2559->mnVBoostState = TAS2559_VBST_DEFAULT;
+ if ((vboost == TAS2559_VBST_DEFAULT)
+ || (vboost == TAS2559_VBST_NEED_DEFAULT)) {
+ dev_dbg(pTAS2559->dev, "%s, already default, bypass\n", __func__);
+ goto end;
+ }
+ }
+
+ dev_dbg(pTAS2559->dev, "vboost: %d\n", vboost);
+
+ if (vboost) {
+ if (pConfiguration->mnDevices & DevA) {
+ nResult = tas2559_update_VBstVolt(pTAS2559, DevA);
+ if (nResult < 0)
+ goto end;
+
+ nResult = pTAS2559->update_bits(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, 0x40, 0x40);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->update_bits(pTAS2559, DevA, TAS2559_SLEEPMODE_CTL_REG, 0x40, 0x00);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState |= TAS2559_VBST_A_ON;
+ dev_dbg(pTAS2559->dev, "%s, devA Boost On, %d\n", __func__, pTAS2559->mnVBoostState);
+ } else {
+ if (pTAS2559->mnVBoostState & TAS2559_VBST_A_ON) {
+ nResult = tas2559_restore_VBstCtl(pTAS2559, DevA);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState &= ~TAS2559_VBST_A_ON;
+ dev_dbg(pTAS2559->dev, "%s, devA Boost Off, %d\n", __func__, pTAS2559->mnVBoostState);
+ }
+ }
+
+ if (pConfiguration->mnDevices & DevB) {
+ nResult = tas2559_update_VBstVolt(pTAS2559, DevB);
+ if (nResult < 0)
+ goto end;
+
+ if (!(pTAS2559->mnVBoostState & TAS2559_VBST_B_ON)) {
+ nResult = pTAS2559->update_bits(pTAS2559, DevB, TAS2560_VBOOST_CTL_REG, 0x01, 0x01);
+ if (nResult < 0)
+ goto end;
+ nResult = pTAS2559->update_bits(pTAS2559, DevB, TAS2560_SLEEPMODE_CTL_REG, 0x08, 0x08);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState |= TAS2559_VBST_B_ON;
+ }
+ dev_dbg(pTAS2559->dev, "%s, devB Boost On, %d\n", __func__, pTAS2559->mnVBoostState);
+ } else {
+ if (pTAS2559->mnVBoostState & TAS2559_VBST_B_ON) {
+ nResult = tas2559_restore_VBstCtl(pTAS2559, DevB);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState &= ~TAS2559_VBST_B_ON;
+ dev_dbg(pTAS2559->dev, "%s, devB Boost Off, %d\n", __func__, pTAS2559->mnVBoostState);
+ }
+ }
+ } else {
+ if (pTAS2559->mnVBoostState & TAS2559_VBST_A_ON) {
+ nResult = tas2559_restore_VBstCtl(pTAS2559, DevA);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState &= ~TAS2559_VBST_A_ON;
+ dev_dbg(pTAS2559->dev, "%s, devA Boost default, %d\n", __func__, pTAS2559->mnVBoostState);
+ }
+ if (pTAS2559->mnVBoostState & TAS2559_VBST_B_ON) {
+ nResult = tas2559_restore_VBstCtl(pTAS2559, DevB);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mnVBoostState &= ~TAS2559_VBST_B_ON;
+ dev_dbg(pTAS2559->dev, "%s, devB Boost default, %d\n", __func__, pTAS2559->mnVBoostState);
+ }
+ }
+
+ pTAS2559->mbLoadVBoostPrePowerUp = true;
+ pTAS2559->mnVBoostNewState = pTAS2559->mnVBoostState;
+
+end:
+
+ return 0;
+}
+
+int tas2559_load_platdata(struct tas2559_priv *pTAS2559)
+{
+ int nResult = 0;
+ int nDev = 0;
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ))
+ nDev |= DevA;
+
+ if (gpio_is_valid(pTAS2559->mnDevBGPIOIRQ))
+ nDev |= DevB;
+
+ if (nDev) {
+ nResult = tas2559_configIRQ(pTAS2559, nDev);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ nResult = tas2559_set_bit_rate(pTAS2559, pTAS2559->mnBitRate);
+
+ if (nResult < 0)
+ goto end;
+
+ nResult = tas2559_SA_ctl_echoRef(pTAS2559);
+
+ if (nResult < 0)
+ goto end;
+
+end:
+
+ return nResult;
+}
+
+int tas2559_load_default(struct tas2559_priv *pTAS2559)
+{
+ int nResult = 0;
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ nResult = tas2559_dev_load_data(pTAS2559, DevBoth, p_tas2559_default_data);
+
+ if (nResult < 0)
+ goto end;
+
+ nResult = tas2559_load_platdata(pTAS2559);
+
+ if (nResult < 0)
+ goto end;
+
+ /* enable DOUT tri-state for extra BCLKs */
+ nResult = pTAS2559->update_bits(pTAS2559, DevA, TAS2559_ASI1_DAC_FORMAT_REG, 0x01, 0x01);
+
+ if (nResult < 0)
+ goto end;
+
+ nResult = pTAS2559->update_bits(pTAS2559, DevB, TAS2560_ASI_CFG_1, 0x02, 0x02);
+
+ if (nResult < 0)
+ goto end;
+
+ /* Interrupt pin, low-highZ, high active driven */
+ nResult = pTAS2559->update_bits(pTAS2559, DevA, TAS2559_GPIO_HIZ_CTRL2_REG, 0x30, 0x30);
+
+end:
+
+ return nResult;
+}
+
+static void failsafe(struct tas2559_priv *pTAS2559)
+{
+ dev_err(pTAS2559->dev, "%s\n", __func__);
+ pTAS2559->mnErrCode |= ERROR_FAILSAFE;
+
+ if (hrtimer_active(&pTAS2559->mtimer))
+ hrtimer_cancel(&pTAS2559->mtimer);
+
+ if(pTAS2559->mnRestart < RESTART_MAX)
+ {
+ pTAS2559->mnRestart ++;
+ msleep(100);
+ dev_err(pTAS2559->dev, "I2C COMM error, restart SmartAmp.\n");
+ schedule_delayed_work(&pTAS2559->irq_work, msecs_to_jiffies(100));
+ return;
+ }
+
+ pTAS2559->enableIRQ(pTAS2559, DevBoth, false);
+ tas2559_DevShutdown(pTAS2559, DevBoth);
+ pTAS2559->mbPowerUp = false;
+ pTAS2559->hw_reset(pTAS2559);
+ pTAS2559->write(pTAS2559, DevBoth, TAS2559_SW_RESET_REG, 0x01);
+ msleep(1);
+ pTAS2559->write(pTAS2559, DevA, TAS2559_SPK_CTRL_REG, 0x04);
+ pTAS2559->write(pTAS2559, DevB, TAS2560_SPK_CTRL_REG, 0x50);
+
+ if (pTAS2559->mpFirmware != NULL)
+ tas2559_clear_firmware(pTAS2559->mpFirmware);
+}
+
+int tas2559_checkPLL(struct tas2559_priv *pTAS2559)
+{
+ int nResult = 0;
+ /*
+ * TO DO
+ */
+
+ return nResult;
+}
+
+/*
+* tas2559_load_coefficient
+*/
+static int tas2559_load_coefficient(struct tas2559_priv *pTAS2559,
+ int nPrevConfig, int nNewConfig, bool bPowerOn)
+{
+ int nResult = 0;
+ struct TPLL *pPLL;
+ struct TProgram *pProgram = NULL;
+ struct TConfiguration *pPrevConfiguration;
+ struct TConfiguration *pNewConfiguration;
+ enum channel chl;
+ bool bRestorePower = false;
+
+ dev_dbg(pTAS2559->dev, "%s, Prev=%d, new=%d, Pow=%d\n",
+ __func__, nPrevConfig, nNewConfig, bPowerOn);
+
+ if (!pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ if (nNewConfig >= pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "%s, invalid configuration New=%d, total=%d\n",
+ __func__, nNewConfig, pTAS2559->mpFirmware->mnConfigurations);
+ goto end;
+ }
+
+ if (nPrevConfig < 0) {
+ pPrevConfiguration = NULL;
+ chl = DevBoth;
+ } else
+ if (nPrevConfig == nNewConfig) {
+ dev_dbg(pTAS2559->dev, "%d configuration is already loaded\n", nNewConfig);
+ goto end;
+ } else {
+ pPrevConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[nPrevConfig]);
+ chl = pPrevConfiguration->mnDevices;
+ }
+
+ pNewConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[nNewConfig]);
+ pTAS2559->mnCurrentConfiguration = nNewConfig;
+
+ if (pPrevConfiguration) {
+ if ((pPrevConfiguration->mnPLL == pNewConfiguration->mnPLL)
+ && (pPrevConfiguration->mnDevices == pNewConfiguration->mnDevices)) {
+ dev_dbg(pTAS2559->dev, "%s, PLL and device same\n", __func__);
+ goto prog_coefficient;
+ }
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+
+ if (bPowerOn) {
+ if (hrtimer_active(&pTAS2559->mtimer))
+ hrtimer_cancel(&pTAS2559->mtimer);
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE)
+ pTAS2559->enableIRQ(pTAS2559, DevBoth, false);
+
+ nResult = tas2559_DevShutdown(pTAS2559, chl);
+
+ if (nResult < 0)
+ goto end;
+
+ bRestorePower = true;
+ }
+
+ /* load PLL */
+ pPLL = &(pTAS2559->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]);
+ dev_dbg(pTAS2559->dev, "load PLL: %s block for Configuration %s\n",
+ pPLL->mpName, pNewConfiguration->mpName);
+ nResult = tas2559_load_block(pTAS2559, &(pPLL->mBlock));
+
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
+
+ dev_dbg(pTAS2559->dev, "load configuration %s conefficient pre block\n",
+ pNewConfiguration->mpName);
+
+ if (pNewConfiguration->mnDevices & DevA) {
+ nResult = tas2559_load_data(pTAS2559, &(pNewConfiguration->mData), TAS2559_BLOCK_CFG_PRE_DEV_A);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (pNewConfiguration->mnDevices & DevB) {
+ nResult = tas2559_load_data(pTAS2559, &(pNewConfiguration->mData), TAS2559_BLOCK_CFG_PRE_DEV_B);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+prog_coefficient:
+ dev_dbg(pTAS2559->dev, "load new configuration: %s, coeff block data\n",
+ pNewConfiguration->mpName);
+
+ if (pNewConfiguration->mnDevices & DevA) {
+ nResult = tas2559_load_data(pTAS2559, &(pNewConfiguration->mData),
+ TAS2559_BLOCK_CFG_COEFF_DEV_A);
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (pNewConfiguration->mnDevices & DevB) {
+ nResult = tas2559_load_data(pTAS2559, &(pNewConfiguration->mData),
+ TAS2559_BLOCK_CFG_COEFF_DEV_B);
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (pTAS2559->mnChannelState == TAS2559_AD_BD) {
+ nResult = pTAS2559->bulk_read(pTAS2559,
+ DevA, TAS2559_SA_CHL_CTRL_REG, pTAS2559->mnDefaultChlData, 16);
+ if (nResult < 0)
+ goto end;
+ } else {
+ nResult = tas2559_SA_DevChnSetup(pTAS2559, pTAS2559->mnChannelState);
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (pTAS2559->mpCalFirmware->mnCalibrations) {
+ nResult = tas2559_set_calibration(pTAS2559, pTAS2559->mnCurrentCalibration);
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (bRestorePower) {
+ dev_dbg(pTAS2559->dev, "%s, set vboost, before power on %d\n",
+ __func__, pTAS2559->mnVBoostState);
+ nResult = tas2559_set_VBoost(pTAS2559, pTAS2559->mnVBoostState, false);
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->clearIRQ(pTAS2559);
+ nResult = tas2559_DevStartup(pTAS2559, pNewConfiguration->mnDevices);
+ if (nResult < 0)
+ goto end;
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ nResult = tas2559_checkPLL(pTAS2559);
+
+ if (nResult < 0) {
+ nResult = tas2559_DevShutdown(pTAS2559, pNewConfiguration->mnDevices);
+ pTAS2559->mbPowerUp = false;
+ goto end;
+ }
+ }
+
+ if (pNewConfiguration->mnDevices & DevB) {
+ nResult = tas2559_load_data(pTAS2559, &(pNewConfiguration->mData),
+ TAS2559_BLOCK_PST_POWERUP_DEV_B);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ dev_dbg(pTAS2559->dev,
+ "device powered up, load unmute\n");
+ nResult = tas2559_DevMute(pTAS2559, pNewConfiguration->mnDevices, false);
+
+ if (nResult < 0)
+ goto end;
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ pTAS2559->enableIRQ(pTAS2559, pNewConfiguration->mnDevices, true);
+
+ if (!hrtimer_active(&pTAS2559->mtimer)) {
+ pTAS2559->mnDieTvReadCounter = 0;
+ hrtimer_start(&pTAS2559->mtimer,
+ ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ }
+ }
+
+end:
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s, load new conf %s error\n", __func__, pNewConfiguration->mpName);
+
+ pTAS2559->mnNewConfiguration = pTAS2559->mnCurrentConfiguration;
+ return nResult;
+}
+
+int tas2559_enable(struct tas2559_priv *pTAS2559, bool bEnable)
+{
+ int nResult = 0;
+ struct TProgram *pProgram;
+ struct TConfiguration *pConfiguration;
+ unsigned int nValue;
+
+ dev_dbg(pTAS2559->dev, "%s: %s\n", __func__, bEnable ? "On" : "Off");
+
+ if ((pTAS2559->mpFirmware->mnPrograms == 0)
+ || (pTAS2559->mpFirmware->mnConfigurations == 0)) {
+ dev_err(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ /*Load firmware*/
+ nResult = request_firmware_nowait(THIS_MODULE, 1, TAS2559_FW_NAME,
+ pTAS2559->dev, GFP_KERNEL, pTAS2559, tas2559_fw_ready);
+ if(nResult < 0) {
+ dev_err(pTAS2559->dev, "%s, firmware is loaded\n", __func__);
+ goto end;
+ }
+ }
+
+ /* check safe guard*/
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_SAFE_GUARD_REG, &nValue);
+ if (nResult < 0)
+ goto end;
+ if ((nValue & 0xff) != TAS2559_SAFE_GUARD_PATTERN) {
+ dev_err(pTAS2559->dev, "ERROR DevA safe guard (0x%x) failure!\n", nValue);
+ nResult = -EPIPE;
+ pTAS2559->mnErrCode = ERROR_SAFE_GUARD;
+ pTAS2559->mbPowerUp = true;
+ goto end;
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+ if (bEnable) {
+ if (!pTAS2559->mbPowerUp) {
+ if (!pTAS2559->mbCalibrationLoaded) {
+ tas2559_set_calibration(pTAS2559, 0xFF);
+ pTAS2559->mbCalibrationLoaded = true;
+ }
+
+ pTAS2559->read(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, &nValue);
+ dev_dbg(pTAS2559->dev, "VBoost ctrl register before coeff set: 0x%x\n", nValue);
+
+ if (pTAS2559->mbLoadConfigurationPrePowerUp) {
+ pTAS2559->mbLoadConfigurationPrePowerUp = false;
+ nResult = tas2559_load_coefficient(pTAS2559,
+ pTAS2559->mnCurrentConfiguration, pTAS2559->mnNewConfiguration, false);
+ if (pTAS2559->mnCurrentConfiguration != pTAS2559->mnNewConfiguration) {
+ pTAS2559->mbLoadVBoostPrePowerUp = true;
+ }
+ if (nResult < 0)
+ goto end;
+ }
+
+ pTAS2559->read(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, &nValue);
+ dev_dbg(pTAS2559->dev, "VBoost ctrl register after coeff set: 0x%x\n", nValue);
+
+ if (pTAS2559->mbLoadVBoostPrePowerUp) {
+ dev_dbg(pTAS2559->dev, "%s, cfg boost before power on new %d, current=%d\n",
+ __func__, pTAS2559->mnVBoostNewState, pTAS2559->mnVBoostState);
+ nResult = tas2559_set_VBoost(pTAS2559, pTAS2559->mnVBoostNewState, false);
+ if (nResult < 0)
+ goto end;
+ pTAS2559->mbLoadVBoostPrePowerUp = false;
+ }
+
+ pTAS2559->read(pTAS2559, DevA, TAS2559_VBOOST_CTL_REG, &nValue);
+ dev_dbg(pTAS2559->dev, "VBoost ctrl register after set VBoost: 0x%x\n", nValue);
+
+ pTAS2559->clearIRQ(pTAS2559);
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+ nResult = tas2559_DevStartup(pTAS2559, pConfiguration->mnDevices);
+ if (nResult < 0)
+ goto end;
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ nResult = tas2559_checkPLL(pTAS2559);
+ if (nResult < 0) {
+ nResult = tas2559_DevShutdown(pTAS2559, pConfiguration->mnDevices);
+ goto end;
+ }
+ }
+
+ if (pConfiguration->mnDevices & DevB) {
+ nResult = tas2559_load_data(pTAS2559, &(pConfiguration->mData),
+ TAS2559_BLOCK_PST_POWERUP_DEV_B);
+ if (nResult < 0)
+ goto end;
+ }
+
+ nResult = tas2559_DevMute(pTAS2559, pConfiguration->mnDevices, false);
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mbPowerUp = true;
+
+ tas2559_get_die_temperature(pTAS2559, &nValue);
+ if(nValue == 0x80000000)
+ {
+ dev_err(pTAS2559->dev, "%s, thermal sensor is wrong, mute output\n", __func__);
+ nResult = tas2559_DevShutdown(pTAS2559, pConfiguration->mnDevices);
+ pTAS2559->mbPowerUp = false;
+ goto end;
+ }
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ /* turn on IRQ */
+ pTAS2559->enableIRQ(pTAS2559, pConfiguration->mnDevices, true);
+ if (!hrtimer_active(&pTAS2559->mtimer)) {
+ pTAS2559->mnDieTvReadCounter = 0;
+ hrtimer_start(&pTAS2559->mtimer,
+ ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ }
+
+ pTAS2559->mnRestart = 0;
+ }
+ } else {
+ if (pTAS2559->mbPowerUp) {
+ if (hrtimer_active(&pTAS2559->mtimer))
+ hrtimer_cancel(&pTAS2559->mtimer);
+
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ /* turn off IRQ */
+ pTAS2559->enableIRQ(pTAS2559, DevBoth, false);
+ }
+
+ nResult = tas2559_DevShutdown(pTAS2559, pConfiguration->mnDevices);
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mbPowerUp = false;
+ pTAS2559->mnRestart = 0;
+ }
+ }
+
+ nResult = 0;
+
+end:
+
+ if (nResult < 0) {
+ if (pTAS2559->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK | ERROR_SAFE_GUARD))
+ failsafe(pTAS2559);
+ }
+
+ dev_dbg(pTAS2559->dev, "%s: exit\n", __func__);
+ return nResult;
+}
+
+int tas2559_set_sampling_rate(struct tas2559_priv *pTAS2559, unsigned int nSamplingRate)
+{
+ int nResult = 0;
+ struct TConfiguration *pConfiguration;
+ unsigned int nConfiguration;
+
+ dev_dbg(pTAS2559->dev, "%s: nSamplingRate = %d [Hz]\n", __func__,
+ nSamplingRate);
+
+ if ((!pTAS2559->mpFirmware->mpPrograms) ||
+ (!pTAS2559->mpFirmware->mpConfigurations)) {
+ dev_err(pTAS2559->dev, "Firmware not loaded\n");
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+
+ if (pConfiguration->mnSamplingRate == nSamplingRate) {
+ dev_info(pTAS2559->dev, "Sampling rate for current configuration matches: %d\n",
+ nSamplingRate);
+ nResult = 0;
+ goto end;
+ }
+
+ for (nConfiguration = 0;
+ nConfiguration < pTAS2559->mpFirmware->mnConfigurations;
+ nConfiguration++) {
+ pConfiguration =
+ &(pTAS2559->mpFirmware->mpConfigurations[nConfiguration]);
+
+ if ((pConfiguration->mnSamplingRate == nSamplingRate)
+ && (pConfiguration->mnProgram == pTAS2559->mnCurrentProgram)) {
+ dev_info(pTAS2559->dev,
+ "Found configuration: %s, with compatible sampling rate %d\n",
+ pConfiguration->mpName, nSamplingRate);
+ nResult = tas2559_load_configuration(pTAS2559, nConfiguration, false);
+ goto end;
+ }
+ }
+
+ dev_err(pTAS2559->dev, "Cannot find a configuration that supports sampling rate: %d\n",
+ nSamplingRate);
+
+end:
+
+ return nResult;
+}
+
+static void fw_print_header(struct tas2559_priv *pTAS2559, struct TFirmware *pFirmware)
+{
+ dev_info(pTAS2559->dev, "FW Size = %d", pFirmware->mnFWSize);
+ dev_info(pTAS2559->dev, "Checksum = 0x%04X", pFirmware->mnChecksum);
+ dev_info(pTAS2559->dev, "PPC Version = 0x%04X", pFirmware->mnPPCVersion);
+ dev_info(pTAS2559->dev, "FW Version = 0x%04X", pFirmware->mnFWVersion);
+ dev_info(pTAS2559->dev, "Driver Version= 0x%04X", pFirmware->mnDriverVersion);
+ dev_info(pTAS2559->dev, "Timestamp = %d", pFirmware->mnTimeStamp);
+ dev_info(pTAS2559->dev, "DDC Name = %s", pFirmware->mpDDCName);
+ dev_info(pTAS2559->dev, "Description = %s", pFirmware->mpDescription);
+}
+
+static 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 tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+ unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 };
+
+ if (nSize < 104) {
+ dev_err(pTAS2559->dev, "Firmware: Header too short");
+ return -EINVAL;
+ }
+
+ if (memcmp(pData, pMagicNumber, 4)) {
+ dev_err(pTAS2559->dev, "Firmware: Magic number doesn't match");
+ return -EINVAL;
+ }
+
+ 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);
+ dev_err(pTAS2559->dev, "Firmware driver: 0x%x", pFirmware->mnDriverVersion);
+ 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(pTAS2559->dev, "Firmware: Header too short after DDC description");
+ return -EINVAL;
+ }
+
+ pFirmware->mnDeviceFamily = fw_convert_number(pData);
+ pData += 4;
+
+ if (pFirmware->mnDeviceFamily != 0) {
+ dev_err(pTAS2559->dev,
+ "deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily);
+ return -EINVAL;
+ }
+
+ pFirmware->mnDevice = fw_convert_number(pData);
+ pData += 4;
+
+ if (pFirmware->mnDevice != 4) {
+ dev_err(pTAS2559->dev,
+ "device %d, not TAS2559", pFirmware->mnDevice);
+ return -EINVAL;
+ }
+
+ fw_print_header(pTAS2559, pFirmware);
+
+ return pData - pDataStart;
+}
+
+static int fw_parse_block_data(struct tas2559_priv *pTAS2559, struct TFirmware *pFirmware,
+ struct TBlock *pBlock, unsigned char *pData)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+
+ pBlock->mnType = fw_convert_number(pData);
+ pData += 4;
+
+ if (pFirmware->mnDriverVersion >= PPC_DRIVER_CRCCHK) {
+ pBlock->mbPChkSumPresent = pData[0];
+ pData++;
+
+ pBlock->mnPChkSum = pData[0];
+ pData++;
+
+ pBlock->mbYChkSumPresent = pData[0];
+ pData++;
+
+ pBlock->mnYChkSum = pData[0];
+ pData++;
+ } else {
+ pBlock->mbPChkSumPresent = 0;
+ pBlock->mbYChkSumPresent = 0;
+ }
+
+ 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(struct tas2559_priv *pTAS2559, struct TFirmware *pFirmware,
+ struct 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(struct TBlock) * pImageData->mnBlocks, GFP_KERNEL);
+
+ for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+ n = fw_parse_block_data(pTAS2559, pFirmware,
+ &(pImageData->mpBlocks[nBlock]), pData);
+ pData += n;
+ }
+
+ return pData - pDataStart;
+}
+
+static int fw_parse_pll_data(struct tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+ unsigned int nPLL;
+ struct TPLL *pPLL;
+
+ pFirmware->mnPLLs = (pData[0] << 8) + pData[1];
+ pData += 2;
+
+ if (pFirmware->mnPLLs == 0)
+ goto end;
+
+ pFirmware->mpPLLs = kmalloc_array(pFirmware->mnPLLs, sizeof(struct TPLL), 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(pTAS2559, pFirmware, &(pPLL->mBlock), pData);
+ pData += n;
+ }
+
+end:
+ return pData - pDataStart;
+}
+
+static int fw_parse_program_data(struct tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+ unsigned int nProgram;
+ struct TProgram *pProgram;
+
+ pFirmware->mnPrograms = (pData[0] << 8) + pData[1];
+ pData += 2;
+
+ if (pFirmware->mnPrograms == 0)
+ goto end;
+
+ pFirmware->mpPrograms =
+ kmalloc(sizeof(struct 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(pTAS2559, pFirmware, &(pProgram->mData), pData);
+ pData += n;
+ }
+
+end:
+
+ return pData - pDataStart;
+}
+
+static int fw_parse_configuration_data(struct tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+ unsigned int nConfiguration;
+ struct TConfiguration *pConfiguration;
+
+ pFirmware->mnConfigurations = (pData[0] << 8) + pData[1];
+ pData += 2;
+
+ if (pFirmware->mnConfigurations == 0)
+ goto end;
+
+ pFirmware->mpConfigurations =
+ kmalloc(sizeof(struct 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;
+
+ if ((pFirmware->mnDriverVersion >= PPC_DRIVER_CONFDEV)
+ || ((pFirmware->mnDriverVersion >= PPC_DRIVER_CFGDEV_NONCRC)
+ && (pFirmware->mnDriverVersion < PPC_DRIVER_CRCCHK))) {
+ pConfiguration->mnDevices = (pData[0] << 8) + pData[1];
+ pData += 2;
+ } else
+ pConfiguration->mnDevices = DevBoth;
+
+ pConfiguration->mnProgram = pData[0];
+ pData++;
+
+ pConfiguration->mnPLL = pData[0];
+ pData++;
+
+ pConfiguration->mnSamplingRate = fw_convert_number(pData);
+ pData += 4;
+
+ if (pFirmware->mnDriverVersion >= PPC_DRIVER_MTPLLSRC) {
+ pConfiguration->mnPLLSrc = pData[0];
+ pData++;
+
+ pConfiguration->mnPLLSrcRate = fw_convert_number(pData);
+ pData += 4;
+ dev_err(pTAS2559->dev, "line:%d, pData: 0x%x, 0x%x, 0x%x, 0x%x", __LINE__, pData[0], pData[1], pData[2], pData[3]);
+ }
+
+ n = fw_parse_data(pTAS2559, pFirmware, &(pConfiguration->mData), pData);
+ pData += n;
+ }
+
+end:
+
+ return pData - pDataStart;
+}
+
+static int fw_parse_calibration_data(struct tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData)
+{
+ unsigned char *pDataStart = pData;
+ unsigned int n;
+ unsigned int nCalibration;
+ struct TCalibration *pCalibration;
+
+ pFirmware->mnCalibrations = (pData[0] << 8) + pData[1];
+ pData += 2;
+
+ if (pFirmware->mnCalibrations == 0)
+ goto end;
+
+ pFirmware->mpCalibrations =
+ kmalloc(sizeof(struct 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_data(pTAS2559, pFirmware, &(pCalibration->mData), pData);
+ pData += n;
+ }
+
+end:
+
+ return pData - pDataStart;
+}
+
+static int fw_parse(struct tas2559_priv *pTAS2559,
+ struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+ int nPosition = 0;
+
+ nPosition = fw_parse_header(pTAS2559, pFirmware, pData, nSize);
+
+ if (nPosition < 0) {
+ dev_err(pTAS2559->dev, "Firmware: Wrong Header");
+ return -EINVAL;
+ }
+
+ if (nPosition >= nSize) {
+ dev_err(pTAS2559->dev, "Firmware: Too short");
+ return -EINVAL;
+ }
+
+ pData += nPosition;
+ nSize -= nPosition;
+ nPosition = 0;
+
+ nPosition = fw_parse_pll_data(pTAS2559, pFirmware, pData);
+
+ pData += nPosition;
+ nSize -= nPosition;
+ nPosition = 0;
+
+ nPosition = fw_parse_program_data(pTAS2559, pFirmware, pData);
+
+ pData += nPosition;
+ nSize -= nPosition;
+ nPosition = 0;
+
+ nPosition = fw_parse_configuration_data(pTAS2559, pFirmware, pData);
+
+ pData += nPosition;
+ nSize -= nPosition;
+ nPosition = 0;
+
+ if (nSize > 64)
+ nPosition = fw_parse_calibration_data(pTAS2559, pFirmware, pData);
+
+ return 0;
+}
+
+
+static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = {
+ 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE, 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C,
+ 0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07, 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5,
+ 0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1, 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43,
+ 0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18, 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA,
+ 0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90, 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62,
+ 0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39, 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB,
+ 0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F, 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D,
+ 0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26, 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4,
+ 0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2, 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20,
+ 0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B, 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89,
+ 0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD, 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F,
+ 0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64, 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96,
+ 0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC, 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E,
+ 0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45, 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7,
+ 0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3, 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01,
+ 0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A, 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8
+};
+
+static int DevAPageYRAM(struct tas2559_priv *pTAS2559,
+ struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (nBook == TAS2559_YRAM_BOOK1) {
+ if (nPage == TAS2559_YRAM1_PAGE) {
+ if (nReg >= TAS2559_YRAM1_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else if ((nReg + len) > TAS2559_YRAM1_START_REG) {
+ pCRCData->mnOffset = TAS2559_YRAM1_START_REG;
+ pCRCData->mnLen = len - (TAS2559_YRAM1_START_REG - nReg);
+ nResult = 1;
+ } else
+ nResult = 0;
+ } else if (nPage == TAS2559_YRAM3_PAGE) {
+ if (nReg > TAS2559_YRAM3_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2559_YRAM3_START_REG) {
+ if ((nReg + len) > TAS2559_YRAM3_END_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = TAS2559_YRAM3_END_REG - nReg + 1;
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ }
+ } else {
+ if ((nReg + (len - 1)) < TAS2559_YRAM3_START_REG) {
+ nResult = 0;
+ } else if ((nReg + (len - 1)) <= TAS2559_YRAM3_END_REG) {
+ pCRCData->mnOffset = TAS2559_YRAM3_START_REG;
+ pCRCData->mnLen = len - (TAS2559_YRAM3_START_REG - nReg);
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = TAS2559_YRAM3_START_REG;
+ pCRCData->mnLen = TAS2559_YRAM3_END_REG - TAS2559_YRAM3_START_REG + 1;
+ nResult = 1;
+ }
+ }
+ }
+ } else if (nBook == TAS2559_YRAM_BOOK2) {
+ if (nPage == TAS2559_YRAM5_PAGE) {
+ if (nReg > TAS2559_YRAM5_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2559_YRAM5_START_REG) {
+ if ((nReg + len) > TAS2559_YRAM5_END_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = TAS2559_YRAM5_END_REG - nReg + 1;
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ }
+ } else {
+ if ((nReg + (len - 1)) < TAS2559_YRAM5_START_REG) {
+ nResult = 0;
+ } else if ((nReg + (len - 1)) <= TAS2559_YRAM5_END_REG) {
+ pCRCData->mnOffset = TAS2559_YRAM5_START_REG;
+ pCRCData->mnLen = len - (TAS2559_YRAM5_START_REG - nReg);
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = TAS2559_YRAM5_START_REG;
+ pCRCData->mnLen = TAS2559_YRAM5_END_REG - TAS2559_YRAM5_START_REG + 1;
+ nResult = 1;
+ }
+ }
+ }
+ } else if (nBook == TAS2559_YRAM_BOOK3) {
+ if (nPage == TAS2559_YRAM6_PAGE) {
+ if (nReg > TAS2559_YRAM6_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2559_YRAM6_START_REG) {
+ if ((nReg + len) > TAS2559_YRAM6_END_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = TAS2559_YRAM6_END_REG - nReg + 1;
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ }
+ } else {
+ if ((nReg + (len - 1)) < TAS2559_YRAM6_START_REG) {
+ nResult = 0;
+ } else if ((nReg + (len - 1)) <= TAS2559_YRAM6_END_REG) {
+ pCRCData->mnOffset = TAS2559_YRAM6_START_REG;
+ pCRCData->mnLen = len - (TAS2559_YRAM6_START_REG - nReg);
+ nResult = 1;
+ } else {
+ pCRCData->mnOffset = TAS2559_YRAM6_START_REG;
+ pCRCData->mnLen = TAS2559_YRAM6_END_REG - TAS2559_YRAM6_START_REG + 1;
+ nResult = 1;
+ }
+ }
+ }
+ } else {
+ nResult = 0;
+ }
+
+ return nResult;
+}
+
+static int isInPageYRAM(struct tas2559_priv *pTAS2559,
+ enum channel dev, struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (dev == DevA)
+ nResult = DevAPageYRAM(pTAS2559, pCRCData, nBook, nPage, nReg, len);
+
+ return nResult;
+}
+
+static int DevABlockYRAM(struct tas2559_priv *pTAS2559,
+ struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (nBook == TAS2559_YRAM_BOOK1) {
+ if (nPage < TAS2559_YRAM2_START_PAGE)
+ nResult = 0;
+ else if (nPage <= TAS2559_YRAM2_END_PAGE) {
+ if (nReg > TAS2559_YRAM2_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2559_YRAM2_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else {
+ if ((nReg + (len - 1)) < TAS2559_YRAM2_START_REG) {
+ nResult = 0;
+ } else {
+ pCRCData->mnOffset = TAS2559_YRAM2_START_REG;
+ pCRCData->mnLen = nReg + len - TAS2559_YRAM2_START_REG;
+ nResult = 1;
+ }
+ }
+ } else {
+ nResult = 0;
+ }
+ } else if (nBook == TAS2559_YRAM_BOOK2) {
+ if (nPage < TAS2559_YRAM4_START_PAGE) {
+ nResult = 0;
+ } else if (nPage <= TAS2559_YRAM4_END_PAGE) {
+ if ((nPage == TAS2559_PAGE_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nReg == TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ dev_dbg(pTAS2559->dev, "bypass swap\n");
+ nResult = 0;
+ } else if (nReg > TAS2559_YRAM2_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2559_YRAM2_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else {
+ if ((nReg + (len - 1)) < TAS2559_YRAM2_START_REG) {
+ nResult = 0;
+ } else {
+ pCRCData->mnOffset = TAS2559_YRAM2_START_REG;
+ pCRCData->mnLen = nReg + len - TAS2559_YRAM2_START_REG;
+ nResult = 1;
+ }
+ }
+ } else {
+ nResult = 0;
+ }
+ } else {
+ nResult = 0;
+ }
+
+ return nResult;
+}
+
+static int DevBBlockYRAM(struct tas2559_priv *pTAS2559,
+ struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (nBook == TAS2560_YRAM_BOOK) {
+ if (nPage < TAS2560_YRAM_START_PAGE) {
+ nResult = 0;
+ } else if (nPage <= TAS2560_YRAM_END_PAGE) {
+ if (nReg > TAS2560_YRAM_END_REG) {
+ nResult = 0;
+ } else if (nReg >= TAS2560_YRAM_START_REG) {
+ pCRCData->mnOffset = nReg;
+ pCRCData->mnLen = len;
+ nResult = 1;
+ } else {
+ if ((nReg + (len - 1)) < TAS2560_YRAM_START_REG) {
+ nResult = 0;
+ } else {
+ pCRCData->mnOffset = TAS2560_YRAM_START_REG;
+ pCRCData->mnLen = nReg + len - TAS2560_YRAM_START_REG;
+ nResult = 1;
+ }
+ }
+ } else {
+ nResult = 0;
+ }
+ }
+
+ return nResult;
+}
+
+static int isInBlockYRAM(struct tas2559_priv *pTAS2559,
+ enum channel dev, struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult = 0;
+
+ if (dev == DevA)
+ nResult = DevABlockYRAM(pTAS2559, pCRCData, nBook, nPage, nReg, len);
+ else
+ if (dev == DevB)
+ nResult = DevBBlockYRAM(pTAS2559, pCRCData, nBook, nPage, nReg, len);
+
+ return nResult;
+}
+
+
+static int isYRAM(struct tas2559_priv *pTAS2559,
+ enum channel dev, struct TYCRC *pCRCData,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+ int nResult;
+
+ nResult = isInPageYRAM(pTAS2559, dev, pCRCData, nBook, nPage, nReg, len);
+
+ if (nResult == 0)
+ nResult = isInBlockYRAM(pTAS2559, dev, pCRCData, nBook, nPage, nReg, len);
+
+ return nResult;
+}
+
+/*
+ * crc8 - calculate a crc8 over the given input data.
+ *
+ * table: crc table used for calculation.
+ * pdata: pointer to data buffer.
+ * nbytes: number of bytes in data buffer.
+ * crc: previous returned crc8 value.
+ */
+static u8 ti_crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc)
+{
+ /* loop over the buffer data */
+ while (nbytes-- > 0)
+ crc = table[(crc ^ *pdata++) & 0xff];
+
+ return crc;
+}
+
+static int doSingleRegCheckSum(struct tas2559_priv *pTAS2559, enum channel chl,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char nValue)
+{
+ int nResult = 0;
+ struct TYCRC sCRCData;
+ unsigned int nData1 = 0, nData2 = 0;
+ unsigned char nRegVal;
+
+ if (chl == DevA) {
+ if ((nBook == TAS2559_BOOK_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nPage == TAS2559_PAGE_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nReg >= TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG))
+ && (nReg <= (TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG) + 4))) {
+ /* DSP swap command, pass */
+ nResult = 0;
+ goto end;
+ }
+ }
+
+ nResult = isYRAM(pTAS2559, chl, &sCRCData, nBook, nPage, nReg, 1);
+
+ if (nResult == 1) {
+ if (chl == DevA) {
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_REG(nBook, nPage, nReg), &nData1);
+
+ if (nResult < 0)
+ goto end;
+ } else if (chl == DevB) {
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2559_REG(nBook, nPage, nReg), &nData2);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (chl == DevA) {
+ if (nData1 != nValue) {
+ dev_err(pTAS2559->dev,
+ "error2 (line %d),B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ __LINE__, nBook, nPage, nReg, nValue, nData1);
+ nResult = -EAGAIN;
+ pTAS2559->mnErrCode |= ERROR_YRAM_CRCCHK;
+ goto end;
+ }
+
+ nRegVal = nData1;
+ } else if (chl == DevB) {
+ if (nData2 != nValue) {
+ dev_err(pTAS2559->dev,
+ "error (line %d),B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ __LINE__, nBook, nPage, nReg, nValue, nData2);
+ nResult = -EAGAIN;
+ pTAS2559->mnErrCode |= ERROR_YRAM_CRCCHK;
+ goto end;
+ }
+
+ nRegVal = nData2;
+ } else {
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ nResult = ti_crc8(crc8_lookup_table, &nRegVal, 1, 0);
+ }
+
+end:
+
+ return nResult;
+}
+
+static int doMultiRegCheckSum(struct tas2559_priv *pTAS2559, enum channel chl,
+ unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned int len)
+{
+ int nResult = 0, i;
+ unsigned char nCRCChkSum = 0;
+ unsigned char nBuf1[128];
+ unsigned char nBuf2[128];
+ struct TYCRC TCRCData;
+ unsigned char *pRegVal;
+
+ if ((nReg + len - 1) > 127) {
+ nResult = -EINVAL;
+ dev_err(pTAS2559->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((nBook == TAS2559_BOOK_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nPage == TAS2559_PAGE_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nReg == TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /* DSP swap command, pass */
+ nResult = 0;
+ goto end;
+ }
+
+ nResult = isYRAM(pTAS2559, chl, &TCRCData, nBook, nPage, nReg, len);
+
+ if (nResult == 1) {
+ if (len == 1) {
+ dev_err(pTAS2559->dev, "firmware error\n");
+ nResult = -EINVAL;
+ goto end;
+ } else {
+ if (chl == DevA) {
+ nResult = pTAS2559->bulk_read(pTAS2559, DevA,
+ TAS2559_REG(nBook, nPage, TCRCData.mnOffset), nBuf1, TCRCData.mnLen);
+
+ if (nResult < 0)
+ goto end;
+ } else if (chl == DevB) {
+ nResult = pTAS2559->bulk_read(pTAS2559, DevB,
+ TAS2559_REG(nBook, nPage, TCRCData.mnOffset), nBuf2, TCRCData.mnLen);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ if (chl == DevA) {
+ pRegVal = nBuf1;
+ } else if (chl == DevB)
+ pRegVal = nBuf2;
+ else {
+ dev_err(pTAS2559->dev, "channel error %d\n", chl);
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ for (i = 0; i < TCRCData.mnLen; i++) {
+ if ((nBook == TAS2559_BOOK_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && (nPage == TAS2559_PAGE_ID(TAS2559_SA_COEFF_SWAP_REG))
+ && ((i + TCRCData.mnOffset)
+ >= TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG))
+ && ((i + TCRCData.mnOffset)
+ <= (TAS2559_PAGE_REG(TAS2559_SA_COEFF_SWAP_REG) + 4))) {
+ /* DSP swap command, bypass */
+ continue;
+ } else {
+ nCRCChkSum += ti_crc8(crc8_lookup_table, &pRegVal[i], 1, 0);
+ }
+ }
+
+ nResult = nCRCChkSum;
+ }
+ }
+
+end:
+
+ return nResult;
+}
+
+static int tas2559_load_block(struct tas2559_priv *pTAS2559, struct TBlock *pBlock)
+{
+ int nResult = 0;
+ unsigned int nCommand = 0;
+ unsigned char nBook;
+ unsigned char nPage;
+ unsigned char nOffset;
+ unsigned char nData;
+ unsigned int nValue1;
+ unsigned int nLength;
+ unsigned int nSleep;
+ bool bDoYCRCChk = false;
+ enum channel chl;
+ unsigned char nCRCChkSum = 0;
+ int nRetry = 6;
+ unsigned char *pData = pBlock->mpData;
+
+ dev_dbg(pTAS2559->dev, "%s: Type = %d, commands = %d\n", __func__,
+ pBlock->mnType, pBlock->mnCommands);
+
+ if (pBlock->mnType == TAS2559_BLOCK_PLL) {
+ chl = DevA;
+ } else if ((pBlock->mnType == TAS2559_BLOCK_PGM_DEV_A)
+ || (pBlock->mnType == TAS2559_BLOCK_CFG_COEFF_DEV_A)
+ || (pBlock->mnType == TAS2559_BLOCK_CFG_PRE_DEV_A)) {
+ chl = DevA;
+ } else if ((pBlock->mnType == TAS2559_BLOCK_PGM_DEV_B)
+ || (pBlock->mnType == TAS2559_BLOCK_PST_POWERUP_DEV_B)
+ || (pBlock->mnType == TAS2559_BLOCK_CFG_PRE_DEV_B)) {
+ chl = DevB;
+ } else {
+ dev_err(pTAS2559->dev, "block type error %d\n", pBlock->mnType);
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ if (pBlock->mbYChkSumPresent && pTAS2559->mbYCRCEnable)
+ bDoYCRCChk = true;
+
+start:
+
+ if (pBlock->mbPChkSumPresent) {
+ if (chl == DevA) {
+ nResult = pTAS2559->write(pTAS2559, DevA, TAS2559_CRC_RESET_REG, 1);
+ if (nResult < 0)
+ goto end;
+ } else {
+ nResult = pTAS2559->write(pTAS2559, DevB, TAS2560_CRC_CHK_REG, 1);
+ if (nResult < 0)
+ goto end;
+ }
+ }
+
+ if (bDoYCRCChk)
+ nCRCChkSum = 0;
+
+ nCommand = 0;
+
+ 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) {
+ nResult = pTAS2559->write(pTAS2559,
+ chl, TAS2559_REG(nBook, nPage, nOffset), nData);
+ if (nResult < 0)
+ goto end;
+
+ if (bDoYCRCChk) {
+ nResult = doSingleRegCheckSum(pTAS2559,
+ chl, nBook, nPage, nOffset, nData);
+ if (nResult < 0)
+ goto check;
+ nCRCChkSum += (unsigned char)nResult;
+ }
+ } else if (nOffset == 0x81) {
+ 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) {
+ nResult = pTAS2559->bulk_write(pTAS2559,
+ chl, TAS2559_REG(nBook, nPage, nOffset), pData + 3, nLength);
+ if (nResult < 0)
+ goto end;
+
+ if (bDoYCRCChk) {
+ nResult = doMultiRegCheckSum(pTAS2559,
+ chl, nBook, nPage, nOffset, nLength);
+ if (nResult < 0)
+ goto check;
+
+ nCRCChkSum += (unsigned char)nResult;
+ }
+ } else {
+ nResult = pTAS2559->write(pTAS2559,
+ chl, TAS2559_REG(nBook, nPage, nOffset), pData[3]);
+ if (nResult < 0)
+ goto end;
+
+ if (bDoYCRCChk) {
+ nResult = doSingleRegCheckSum(pTAS2559,
+ chl, nBook, nPage, nOffset, pData[3]);
+ if (nResult < 0)
+ goto check;
+
+ nCRCChkSum += (unsigned char)nResult;
+ }
+ }
+
+ nCommand++;
+
+ if (nLength >= 2)
+ nCommand += ((nLength - 2) / 4) + 1;
+ }
+ }
+
+ if (pBlock->mbPChkSumPresent) {
+ if (chl == DevA)
+ nResult = pTAS2559->read(pTAS2559, DevA,
+ TAS2559_CRC_CHECKSUM_REG, &nValue1);
+ else
+ nResult = pTAS2559->read(pTAS2559, DevB,
+ TAS2560_CRC_CHK_REG, &nValue1);
+
+ if (nResult < 0)
+ goto end;
+
+ if (nValue1 != pBlock->mnPChkSum) {
+ dev_err(pTAS2559->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x\n",
+ pBlock->mnPChkSum, (nValue1 & 0xff));
+ nResult = -EAGAIN;
+ pTAS2559->mnErrCode |= ERROR_PRAM_CRCCHK;
+ goto check;
+ }
+
+ nResult = 0;
+ pTAS2559->mnErrCode &= ~ERROR_PRAM_CRCCHK;
+ dev_dbg(pTAS2559->dev, "Block[0x%x] PChkSum match\n", pBlock->mnType);
+ }
+
+ if (bDoYCRCChk) {
+ if (nCRCChkSum != pBlock->mnYChkSum) {
+ dev_err(pTAS2559->dev, "Block YChkSum Error: FW = 0x%x, YCRC = 0x%x\n",
+ pBlock->mnYChkSum, nCRCChkSum);
+ nResult = -EAGAIN;
+ pTAS2559->mnErrCode |= ERROR_YRAM_CRCCHK;
+ goto check;
+ }
+
+ pTAS2559->mnErrCode &= ~ERROR_YRAM_CRCCHK;
+ nResult = 0;
+ dev_dbg(pTAS2559->dev, "Block[0x%x] YChkSum match\n", pBlock->mnType);
+ }
+
+check:
+
+ if (nResult == -EAGAIN) {
+ nRetry--;
+
+ if (nRetry > 0)
+ goto start;
+ }
+
+end:
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "Block (%d) load error\n",
+ pBlock->mnType);
+
+ return nResult;
+}
+
+static int tas2559_load_data(struct tas2559_priv *pTAS2559, struct TData *pData, unsigned int nType)
+{
+ int nResult = 0;
+ unsigned int nBlock;
+ struct TBlock *pBlock;
+
+ dev_dbg(pTAS2559->dev,
+ "TAS2559 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) {
+ nResult = tas2559_load_block(pTAS2559, pBlock);
+
+ if (nResult < 0)
+ break;
+ }
+ }
+
+ return nResult;
+}
+
+static int tas2559_load_configuration(struct tas2559_priv *pTAS2559,
+ unsigned int nConfiguration, bool bLoadSame)
+{
+ int nResult = 0;
+ struct TConfiguration *pCurrentConfiguration = NULL;
+ struct TConfiguration *pNewConfiguration = NULL;
+
+ dev_dbg(pTAS2559->dev, "%s: %d\n", __func__, nConfiguration);
+
+ if ((!pTAS2559->mpFirmware->mpPrograms) ||
+ (!pTAS2559->mpFirmware->mpConfigurations)) {
+ dev_err(pTAS2559->dev, "Firmware not loaded\n");
+ nResult = 0;
+ goto end;
+ }
+
+ if (nConfiguration >= pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "Configuration %d doesn't exist\n",
+ nConfiguration);
+ nResult = 0;
+ goto end;
+ }
+
+ if ((!pTAS2559->mbLoadConfigurationPrePowerUp)
+ && (nConfiguration == pTAS2559->mnCurrentConfiguration)
+ && (!bLoadSame)) {
+ dev_info(pTAS2559->dev, "Configuration %d is already loaded\n",
+ nConfiguration);
+ nResult = 0;
+ goto end;
+ }
+
+ pCurrentConfiguration =
+ &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+ pNewConfiguration =
+ &(pTAS2559->mpFirmware->mpConfigurations[nConfiguration]);
+
+ if (pNewConfiguration->mnProgram != pCurrentConfiguration->mnProgram) {
+ dev_err(pTAS2559->dev, "Configuration %d, %s doesn't share the same program as current %d\n",
+ nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram);
+ nResult = 0;
+ goto end;
+ }
+
+ if (pNewConfiguration->mnPLL >= pTAS2559->mpFirmware->mnPLLs) {
+ dev_err(pTAS2559->dev, "Configuration %d, %s doesn't have a valid PLL index %d\n",
+ nConfiguration, pNewConfiguration->mpName, pNewConfiguration->mnPLL);
+ nResult = 0;
+ goto end;
+ }
+
+ if (pTAS2559->mbPowerUp) {
+ dev_err(pTAS2559->dev, "%s, device power on, load new conf[%d] %s\n", __func__,
+ nConfiguration, pNewConfiguration->mpName);
+ nResult = tas2559_load_coefficient(pTAS2559, pTAS2559->mnCurrentConfiguration, nConfiguration, true);
+ pTAS2559->mbLoadConfigurationPrePowerUp = false;
+ } else {
+ dev_dbg(pTAS2559->dev,
+ "TAS2559 was powered down, will load coefficient when power up\n");
+ pTAS2559->mbLoadConfigurationPrePowerUp = true;
+ pTAS2559->mnNewConfiguration = nConfiguration;
+ }
+
+end:
+
+ if (nResult < 0) {
+ if (pTAS2559->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+ failsafe(pTAS2559);
+ }
+
+ return nResult;
+}
+
+int tas2559_set_config(struct tas2559_priv *pTAS2559, int config)
+{
+ struct TConfiguration *pConfiguration;
+ struct TProgram *pProgram;
+ unsigned int nProgram = pTAS2559->mnCurrentProgram;
+ unsigned int nConfiguration = config;
+ int nResult = 0;
+
+ if ((!pTAS2559->mpFirmware->mpPrograms) ||
+ (!pTAS2559->mpFirmware->mpConfigurations)) {
+ dev_err(pTAS2559->dev, "Firmware not loaded\n");
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ if (nConfiguration >= pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "Configuration %d doesn't exist\n",
+ nConfiguration);
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[nConfiguration]);
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[nProgram]);
+
+ if (nProgram != pConfiguration->mnProgram) {
+ dev_err(pTAS2559->dev,
+ "Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n",
+ nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram,
+ nProgram, pProgram->mpName);
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ dev_dbg(pTAS2559->dev, "%s, load new conf %s\n", __func__, pConfiguration->mpName);
+ nResult = tas2559_load_configuration(pTAS2559, nConfiguration, false);
+
+end:
+
+ return nResult;
+}
+
+void tas2559_clear_firmware(struct TFirmware *pFirmware)
+{
+ unsigned int n, nn;
+
+ if (!pFirmware)
+ return;
+
+ kfree(pFirmware->mpDescription);
+
+ if (pFirmware->mpPLLs != NULL) {
+ for (n = 0; n < pFirmware->mnPLLs; n++) {
+ kfree(pFirmware->mpPLLs[n].mpDescription);
+ kfree(pFirmware->mpPLLs[n].mBlock.mpData);
+ }
+
+ kfree(pFirmware->mpPLLs);
+ }
+
+ if (pFirmware->mpPrograms != NULL) {
+ 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);
+ }
+
+ if (pFirmware->mpConfigurations != NULL) {
+ 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);
+ }
+
+ if (pFirmware->mpCalibrations != NULL) {
+ for (n = 0; n < pFirmware->mnCalibrations; n++) {
+ kfree(pFirmware->mpCalibrations[n].mpDescription);
+ kfree(pFirmware->mpCalibrations[n].mData.mpDescription);
+
+ for (nn = 0; nn < pFirmware->mpCalibrations[n].mData.mnBlocks; nn++)
+ kfree(pFirmware->mpCalibrations[n].mData.mpBlocks[nn].mpData);
+
+ kfree(pFirmware->mpCalibrations[n].mData.mpBlocks);
+ }
+
+ kfree(pFirmware->mpCalibrations);
+ }
+
+ memset(pFirmware, 0x00, sizeof(struct TFirmware));
+}
+
+static int tas2559_load_calibration(struct tas2559_priv *pTAS2559, char *pFileName)
+{
+ int nResult = 0;
+ int nFile;
+ mm_segment_t fs;
+ unsigned char pBuffer[1000];
+ int nSize = 0;
+
+ int flags = O_RDONLY;
+ if (force_o_largefile())
+ flags |= O_LARGEFILE;
+
+ dev_dbg(pTAS2559->dev, "%s:\n", __func__);
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ nFile = do_sys_open(AT_FDCWD, pFileName, O_RDONLY, 0);
+
+ dev_info(pTAS2559->dev, "TAS2559 calibration file = %s, handle = %d\n",
+ pFileName, nFile);
+
+ if (nFile >= 0) {
+ nSize = ksys_read(nFile, pBuffer, 1000);
+ ksys_close(nFile);
+ } else {
+ dev_err(pTAS2559->dev, "TAS2559 cannot open calibration file: %s\n",
+ pFileName);
+ nResult = -EINVAL;
+ }
+
+ set_fs(fs);
+
+ if (!nSize)
+ goto end;
+
+ tas2559_clear_firmware(pTAS2559->mpCalFirmware);
+ dev_info(pTAS2559->dev, "TAS2559 calibration file size = %d\n", nSize);
+ nResult = fw_parse(pTAS2559, pTAS2559->mpCalFirmware, pBuffer, nSize);
+
+ if (nResult)
+ dev_err(pTAS2559->dev, "TAS2559 calibration file is corrupt\n");
+ else
+ dev_info(pTAS2559->dev, "TAS2559 calibration: %d calibrations\n",
+ pTAS2559->mpCalFirmware->mnCalibrations);
+
+end:
+
+ return nResult;
+}
+
+void tas2559_fw_ready(const struct firmware *pFW, void *pContext)
+{
+ struct tas2559_priv *pTAS2559 = (struct tas2559_priv *) pContext;
+ int nResult;
+ unsigned int nProgram = 0;
+ unsigned int nSampleRate = 0;
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_lock(&pTAS2559->codec_lock);
+#endif
+
+ dev_info(pTAS2559->dev, "%s:\n", __func__);
+
+ if (unlikely(!pFW) || unlikely(!pFW->data)) {
+ dev_err(pTAS2559->dev, "%s firmware is not loaded.\n",
+ TAS2559_FW_NAME);
+ goto end;
+ }
+
+ if (pTAS2559->mpFirmware->mpConfigurations) {
+ nProgram = pTAS2559->mnCurrentProgram;
+ nSampleRate = pTAS2559->mnCurrentSampleRate;
+ dev_dbg(pTAS2559->dev, "clear current firmware\n");
+ tas2559_clear_firmware(pTAS2559->mpFirmware);
+ }
+
+ nResult = fw_parse(pTAS2559, pTAS2559->mpFirmware, (unsigned char *)(pFW->data), pFW->size);
+ release_firmware(pFW);
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev, "firmware is corrupt\n");
+ goto end;
+ }
+
+ if (!pTAS2559->mpFirmware->mnPrograms) {
+ dev_err(pTAS2559->dev, "firmware contains no programs\n");
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ if (!pTAS2559->mpFirmware->mnConfigurations) {
+ dev_err(pTAS2559->dev, "firmware contains no configurations\n");
+ nResult = -EINVAL;
+ goto end;
+ }
+
+ if (nProgram >= pTAS2559->mpFirmware->mnPrograms) {
+ dev_info(pTAS2559->dev,
+ "no previous program, set to default\n");
+ nProgram = 0;
+ }
+
+ pTAS2559->mnCurrentSampleRate = nSampleRate;
+ nResult = tas2559_set_program(pTAS2559, nProgram, -1);
+
+end:
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_unlock(&pTAS2559->codec_lock);
+#endif
+}
+
+int tas2559_set_program(struct tas2559_priv *pTAS2559,
+ unsigned int nProgram, int nConfig)
+{
+ struct TProgram *pProgram;
+ struct TConfiguration *pConfiguration;
+ unsigned int nConfiguration = 0;
+ unsigned int nSampleRate = 0;
+ bool bFound = false;
+ int nResult = 0;
+
+ if ((!pTAS2559->mpFirmware->mpPrograms) ||
+ (!pTAS2559->mpFirmware->mpConfigurations)) {
+ dev_err(pTAS2559->dev, "Firmware not loaded\n");
+ nResult = 0;
+ goto end;
+ }
+
+ if (nProgram >= pTAS2559->mpFirmware->mnPrograms) {
+ dev_err(pTAS2559->dev, "TAS2559: Program %d doesn't exist\n",
+ nProgram);
+ nResult = 0;
+ goto end;
+ }
+
+ if(nProgram == 1)
+ pTAS2559->mnCurrentSampleRate = 96000;
+ else
+ pTAS2559->mnCurrentSampleRate = 48000;
+
+ if (nConfig < 0) {
+ nConfiguration = 0;
+ nSampleRate = pTAS2559->mnCurrentSampleRate;
+ dev_err(pTAS2559->dev, "nSampleRate: %d\n", nSampleRate);
+
+ while (!bFound && (nConfiguration < pTAS2559->mpFirmware->mnConfigurations)) {
+ dev_err(pTAS2559->dev, "mpConfigurations SampleRate: %d\n",
+ pTAS2559->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate);
+ if (pTAS2559->mpFirmware->mpConfigurations[nConfiguration].mnProgram == nProgram) {
+ if (nSampleRate == 0) {
+ bFound = true;
+ dev_info(pTAS2559->dev, "find default configuration %d\n", nConfiguration);
+ } else if (nSampleRate == pTAS2559->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate) {
+ bFound = true;
+ dev_info(pTAS2559->dev, "find matching configuration %d\n", nConfiguration);
+ } else {
+ nConfiguration++;
+ }
+ } else {
+ nConfiguration++;
+ }
+ }
+
+ if (!bFound) {
+ dev_err(pTAS2559->dev,
+ "Program %d, no valid configuration found for sample rate %d, ignore\n",
+ nProgram, nSampleRate);
+ nResult = 0;
+ goto end;
+ }
+ } else {
+ if (pTAS2559->mpFirmware->mpConfigurations[nConfig].mnProgram != nProgram) {
+ dev_err(pTAS2559->dev, "%s, configuration program doesn't match\n", __func__);
+ nResult = 0;
+ goto end;
+ }
+
+ nConfiguration = nConfig;
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[nProgram]);
+
+ if (pTAS2559->mbPowerUp) {
+ dev_info(pTAS2559->dev,
+ "device powered up, power down to load program %d (%s)\n",
+ nProgram, pProgram->mpName);
+
+ if (hrtimer_active(&pTAS2559->mtimer))
+ hrtimer_cancel(&pTAS2559->mtimer);
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE)
+ pTAS2559->enableIRQ(pTAS2559, DevBoth, false);
+
+ nResult = tas2559_DevShutdown(pTAS2559, DevBoth);
+
+ if (nResult < 0)
+ goto end;
+ }
+
+ pTAS2559->hw_reset(pTAS2559);
+ nResult = pTAS2559->write(pTAS2559, DevBoth, TAS2559_SW_RESET_REG, 0x01);
+ if (nResult < 0)
+ goto end;
+
+ msleep(1);
+ nResult = tas2559_load_default(pTAS2559);
+ if (nResult < 0)
+ goto end;
+
+ dev_info(pTAS2559->dev, "load program %d (%s)\n", nProgram, pProgram->mpName);
+ nResult = tas2559_load_data(pTAS2559, &(pProgram->mData), TAS2559_BLOCK_PGM_DEV_A);
+ if (nResult < 0)
+ goto end;
+
+ nResult = tas2559_load_data(pTAS2559, &(pProgram->mData), TAS2559_BLOCK_PGM_DEV_B);
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mnCurrentProgram = nProgram;
+ pTAS2559->mnDevGain = 15;
+ pTAS2559->mnDevCurrentGain = 15;
+
+ nResult = tas2559_load_coefficient(pTAS2559, -1, nConfiguration, false);
+ if (nResult < 0)
+ goto end;
+
+ if (pTAS2559->mbPowerUp) {
+ dev_info(pTAS2559->dev, "%s, load VBoost before power on %d\n", __func__, pTAS2559->mnVBoostState);
+ nResult = tas2559_set_VBoost(pTAS2559, pTAS2559->mnVBoostState, false);
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->clearIRQ(pTAS2559);
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+ nResult = tas2559_DevStartup(pTAS2559, pConfiguration->mnDevices);
+ if (nResult < 0)
+ goto end;
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ nResult = tas2559_checkPLL(pTAS2559);
+ if (nResult < 0) {
+ nResult = tas2559_DevShutdown(pTAS2559, pConfiguration->mnDevices);
+ pTAS2559->mbPowerUp = false;
+ goto end;
+ }
+ }
+
+ if (pConfiguration->mnDevices & DevB) {
+ nResult = tas2559_load_data(pTAS2559, &(pConfiguration->mData),
+ TAS2559_BLOCK_PST_POWERUP_DEV_B);
+ if (nResult < 0)
+ goto end;
+ }
+
+ nResult = tas2559_DevMute(pTAS2559, pConfiguration->mnDevices, false);
+ if (nResult < 0)
+ goto end;
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ pTAS2559->enableIRQ(pTAS2559, pConfiguration->mnDevices, true);
+
+ if (!hrtimer_active(&pTAS2559->mtimer)) {
+ pTAS2559->mnDieTvReadCounter = 0;
+ hrtimer_start(&pTAS2559->mtimer,
+ ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ }
+ }
+
+end:
+
+ if (nResult < 0) {
+ if (pTAS2559->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+ failsafe(pTAS2559);
+ }
+
+ return nResult;
+}
+
+int tas2559_set_calibration(struct tas2559_priv *pTAS2559, int nCalibration)
+{
+ struct TCalibration *pCalibration = NULL;
+ struct TConfiguration *pConfiguration;
+ struct TProgram *pProgram;
+ int nResult = 0;
+
+ if ((!pTAS2559->mpFirmware->mpPrograms)
+ || (!pTAS2559->mpFirmware->mpConfigurations)) {
+ dev_err(pTAS2559->dev, "Firmware not loaded\n\r");
+ nResult = 0;
+ goto end;
+ }
+
+ if (nCalibration == 0x00FF) {
+ dev_info(pTAS2559->dev, "load new calibration file %s\n", TAS2559_CAL_NAME);
+ nResult = tas2559_load_calibration(pTAS2559, TAS2559_CAL_NAME);
+
+ if (nResult < 0) {
+ nResult = 0;
+ goto end;
+ }
+
+ nCalibration = 0;
+ }
+
+ if (nCalibration >= pTAS2559->mpCalFirmware->mnCalibrations) {
+ dev_err(pTAS2559->dev,
+ "Calibration %d doesn't exist\n", nCalibration);
+ nResult = 0;
+ goto end;
+ }
+
+ pTAS2559->mnCurrentCalibration = nCalibration;
+
+ if (pTAS2559->mbLoadConfigurationPrePowerUp)
+ goto end;
+
+ pCalibration = &(pTAS2559->mpCalFirmware->mpCalibrations[nCalibration]);
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+
+ if (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE) {
+ dev_dbg(pTAS2559->dev, "Enable: load calibration\n");
+ nResult = tas2559_load_data(pTAS2559, &(pCalibration->mData), TAS2559_BLOCK_CFG_COEFF_DEV_A);
+ }
+
+end:
+
+ if (nResult < 0) {
+ tas2559_clear_firmware(pTAS2559->mpCalFirmware);
+ nResult = tas2559_set_program(pTAS2559, pTAS2559->mnCurrentProgram, pTAS2559->mnCurrentConfiguration);
+ }
+
+ return nResult;
+}
+
+int tas2559_get_Cali_prm_r0(struct tas2559_priv *pTAS2559, enum channel chl, int *prm_r0)
+{
+ int nResult = 0;
+ int n, nn;
+ struct TCalibration *pCalibration;
+ struct TData *pData;
+ struct TBlock *pBlock;
+ int nReg;
+ int nBook, nPage, nOffset;
+ unsigned char *pCommands;
+ int nCali_Re;
+ bool bFound = false;
+ int len;
+
+ if (!pTAS2559->mpCalFirmware->mnCalibrations) {
+ dev_err(pTAS2559->dev, "%s, no calibration data\n", __func__);
+ goto end;
+ }
+
+ if (chl == DevA)
+ nReg = TAS2559_DEVA_CALI_R0_REG;
+ else if (chl == DevB)
+ nReg = TAS2559_DEVB_CALI_R0_REG;
+ else
+ goto end;
+
+ pCalibration = &(pTAS2559->mpCalFirmware->mpCalibrations[pTAS2559->mnCurrentCalibration]);
+ pData = &(pCalibration->mData);
+
+ for (n = 0; n < pData->mnBlocks; n++) {
+ pBlock = &(pData->mpBlocks[n]);
+ pCommands = pBlock->mpData;
+
+ for (nn = 0 ; nn < pBlock->mnCommands;) {
+ nBook = pCommands[4 * nn + 0];
+ nPage = pCommands[4 * nn + 1];
+ nOffset = pCommands[4 * nn + 2];
+
+ if ((nOffset < 0x7f) || (nOffset == 0x81))
+ nn++;
+ else
+ if (nOffset == 0x85) {
+ len = ((int)nBook << 8) | nPage;
+
+ nBook = pCommands[4 * nn + 4];
+ nPage = pCommands[4 * nn + 5];
+ nOffset = pCommands[4 * nn + 6];
+
+ if ((nBook == TAS2559_BOOK_ID(nReg))
+ && (nPage == TAS2559_PAGE_ID(nReg))
+ && (nOffset == TAS2559_PAGE_REG(nReg))) {
+ nCali_Re = ((int)pCommands[4 * nn + 7] << 24)
+ | ((int)pCommands[4 * nn + 8] << 16)
+ | ((int)pCommands[4 * nn + 9] << 8)
+ | (int)pCommands[4 * nn + 10];
+ bFound = true;
+ goto end;
+ }
+
+ nn += 2;
+ nn += ((len - 1) / 4);
+
+ if ((len - 1) % 4)
+ nn++;
+ } else {
+ dev_err(pTAS2559->dev, "%s, format error %d\n", __func__, nOffset);
+ break;
+ }
+ }
+ }
+
+end:
+
+ if (bFound)
+ *prm_r0 = nCali_Re;
+
+ return nResult;
+}
+
+int tas2559_parse_dt(struct device *dev, struct tas2559_priv *pTAS2559)
+{
+ struct device_node *np = dev->of_node;
+ int rc = 0, ret = 0;
+ unsigned int value;
+
+ pTAS2559->mnDevAGPIORST = of_get_named_gpio(np, "ti,tas2559-reset-gpio", 0);
+ if (!gpio_is_valid(pTAS2559->mnDevAGPIORST))
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2559-reset-gpio", np->full_name, pTAS2559->mnDevAGPIORST);
+ else
+ dev_dbg(pTAS2559->dev, "%s, tas2559 reset gpio %d\n", __func__, pTAS2559->mnDevAGPIORST);
+
+ pTAS2559->mnDevBGPIORST = of_get_named_gpio(np, "ti,tas2560-reset-gpio", 0);
+ if (!gpio_is_valid(pTAS2559->mnDevBGPIORST))
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2560-reset-gpio", np->full_name, pTAS2559->mnDevBGPIORST);
+ else
+ dev_dbg(pTAS2559->dev, "%s, tas2560 reset gpio %d\n", __func__, pTAS2559->mnDevBGPIORST);
+
+ pTAS2559->mnDevAGPIOIRQ = of_get_named_gpio(np, "ti,tas2559-irq-gpio", 0);
+ if (!gpio_is_valid(pTAS2559->mnDevAGPIOIRQ))
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2559-irq-gpio", np->full_name, pTAS2559->mnDevAGPIOIRQ);
+
+ pTAS2559->mnDevBGPIOIRQ = of_get_named_gpio(np, "ti,tas2560-irq-gpio", 0);
+ if (!gpio_is_valid(pTAS2559->mnDevBGPIOIRQ))
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2560-irq-gpio", np->full_name, pTAS2559->mnDevBGPIOIRQ);
+
+ rc = of_property_read_u32(np, "ti,tas2559-addr", &value);
+ if (rc) {
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2559-addr", np->full_name, rc);
+ ret = -EINVAL;
+ goto end;
+ } else {
+ pTAS2559->mnDevAAddr = value;
+ dev_dbg(pTAS2559->dev, "ti,tas2559 addr=0x%x\n", pTAS2559->mnDevAAddr);
+ }
+
+ rc = of_property_read_u32(np, "ti,tas2560-addr", &value);
+ if (rc) {
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2560-addr", np->full_name, rc);
+ ret = -EINVAL;
+ goto end;
+ } else {
+ pTAS2559->mnDevBAddr = value;
+ dev_dbg(pTAS2559->dev, "ti,tas2560-addr=0x%x\n", pTAS2559->mnDevBAddr);
+ }
+
+ rc = of_property_read_u32(np, "ti,tas2559-channel", &value);
+ if (rc)
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2559-channel", np->full_name, rc);
+ else{
+ dev_dbg(pTAS2559->dev, "channel-a value : %d\n", value);
+ pTAS2559->mnDevAChl = value;
+ }
+
+
+ rc = of_property_read_u32(np, "ti,tas2560-channel", &value);
+ if (rc)
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,tas2560-channel", np->full_name, rc);
+ else{
+ dev_dbg(pTAS2559->dev, "channel-b value : %d\n", value);
+ pTAS2559->mnDevBChl = value;
+ }
+
+
+ rc = of_property_read_u32(np, "ti,echo-ref", &value);
+ if (rc)
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,echo-ref", np->full_name, rc);
+ else{
+ dev_dbg(pTAS2559->dev, "Echo Ref value : %d\n", value);
+ pTAS2559->mnEchoRef = value;
+ }
+
+ rc = of_property_read_u32(np, "ti,bit-rate", &value);
+ if (rc)
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,i2s-bits", np->full_name, rc);
+ else{
+ dev_dbg(pTAS2559->dev, "Bit Rate value : %d\n", value);
+ pTAS2559->mnBitRate = value;
+ }
+
+
+ rc = of_property_read_u32(np, "ti,ycrc-enable", &value);
+ if (rc)
+ dev_err(pTAS2559->dev, "Looking up %s property in node %s failed %d\n",
+ "ti,ycrc-enable", np->full_name, rc);
+ else{
+ dev_dbg(pTAS2559->dev, "YCRCEnable value : %d\n", value);
+ pTAS2559->mbYCRCEnable = (value != 0);
+ }
+
+
+end:
+
+ return ret;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2559 common functions for Android Linux");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2559-core.h b/sound/soc/codecs/tas2559-core.h
new file mode 100644
index 000000000000..5afbf91ba605
--- /dev/null
+++ b/sound/soc/codecs/tas2559-core.h
@@ -0,0 +1,94 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559-core.h
+**
+** Description:
+** header file for tas2559-core.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2559_CORE_H
+#define _TAS2559_CORE_H
+
+#include "tas2559.h"
+
+#define TAS2559_YRAM_BOOK1 140 /* 0x8c */
+
+#define TAS2559_YRAM1_PAGE 42 /* 0x2a */
+#define TAS2559_YRAM1_START_REG 88
+#define TAS2559_YRAM1_END_REG 127
+
+#define TAS2559_YRAM2_START_PAGE 43 /* 0x2b */
+#define TAS2559_YRAM2_END_PAGE 55
+#define TAS2559_YRAM2_START_REG 8
+#define TAS2559_YRAM2_END_REG 127
+
+#define TAS2559_YRAM3_PAGE 56 /* 0x38 */
+#define TAS2559_YRAM3_START_REG 8
+#define TAS2559_YRAM3_END_REG 47
+
+/* should not include B0_P53_R44-R47 */
+#define TAS2559_YRAM_BOOK2 0
+#define TAS2559_YRAM4_START_PAGE 50 /* 0x32 */
+#define TAS2559_YRAM4_END_PAGE 60 /* 0x3c */
+#define TAS2559_YRAM4_START_REG 8
+#define TAS2559_YRAM4_END_REG 127
+
+#define TAS2559_YRAM5_PAGE 61 /* 0x3d */
+#define TAS2559_YRAM5_START_REG 8
+#define TAS2559_YRAM5_END_REG 27
+
+#define TAS2559_YRAM_BOOK3 120 /* 0x78 */
+#define TAS2559_YRAM6_PAGE 12 /* 0x0c */
+#define TAS2559_YRAM6_START_REG 104 /* 0x68 */
+#define TAS2559_YRAM6_END_REG 107 /* 0x6b */
+
+#define TAS2559_SAFE_GUARD_PATTERN 0x5a
+#define LOW_TEMPERATURE_CHECK_PERIOD 5000 /* 5 second */
+
+struct TYCRC {
+ unsigned char mnOffset;
+ unsigned char mnLen;
+};
+
+int tas2559_DevMuteStatus(struct tas2559_priv *pTAS2559, enum channel dev, bool *pMute);
+int tas2559_DevMute(struct tas2559_priv *pTAS2559, enum channel dev, bool mute);
+int tas2559_SA_ctl_echoRef(struct tas2559_priv *pTAS2559);
+int tas2559_set_VBstVolt(struct tas2559_priv *pTAS2559, unsigned int vbstvolt);
+int tas2559_set_VBoost(struct tas2559_priv *pTAS2559, int vboost, bool bPowerUp);
+int tas2559_get_VBoost(struct tas2559_priv *pTAS2559, int *pVBoost);
+int tas2559_SA_DevChnSetup(struct tas2559_priv *pTAS2559, unsigned int mode);
+int tas2559_get_Cali_prm_r0(struct tas2559_priv *pTAS2559, enum channel chl, int *prm_r0);
+int tas2559_get_die_temperature(struct tas2559_priv *pTAS2559, int *pTemperature);
+int tas2559_enable(struct tas2559_priv *pTAS2559, bool bEnable);
+int tas2559_set_sampling_rate(struct tas2559_priv *pTAS2559,
+ unsigned int nSamplingRate);
+int tas2559_set_bit_rate(struct tas2559_priv *pTAS2559, unsigned int nBitRate);
+int tas2559_get_bit_rate(struct tas2559_priv *pTAS2559, unsigned char *pBitRate);
+int tas2559_set_config(struct tas2559_priv *pTAS2559, int config);
+void tas2559_fw_ready(const struct firmware *pFW, void *pContext);
+int tas2559_set_program(struct tas2559_priv *pTAS2559,
+ unsigned int nProgram, int nConfig);
+int tas2559_set_calibration(struct tas2559_priv *pTAS2559,
+ int nCalibration);
+int tas2559_load_default(struct tas2559_priv *pTAS2559);
+int tas2559_parse_dt(struct device *dev, struct tas2559_priv *pTAS2559);
+int tas2559_get_DAC_gain(struct tas2559_priv *pTAS2559,
+ enum channel chl, unsigned char *pnGain);
+int tas2559_set_DAC_gain(struct tas2559_priv *pTAS2559,
+ enum channel chl, unsigned int nGain);
+int tas2559_configIRQ(struct tas2559_priv *pTAS2559, enum channel dev);
+
+#endif /* _TAS2559_CORE_H */
diff --git a/sound/soc/codecs/tas2559-regmap.c b/sound/soc/codecs/tas2559-regmap.c
new file mode 100644
index 000000000000..386f61e16810
--- /dev/null
+++ b/sound/soc/codecs/tas2559-regmap.c
@@ -0,0 +1,1188 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559-regmap.c
+**
+** Description:
+** I2C driver with regmap for Texas Instruments TAS2559 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#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/uaccess.h>
+#include <linux/interrupt.h>
+#include "tas2559.h"
+#include "tas2559-core.h"
+#include "tas2560.h"
+
+#ifdef CONFIG_TAS2559_CODEC
+#include "tas2559-codec.h"
+#endif
+
+/*
+* tas2559_i2c_write_device : write single byte to device
+* platform dependent, need platform specific support
+*/
+static int tas2559_i2c_write_device(struct tas2559_priv *pTAS2559,
+ unsigned char addr,
+ unsigned char reg,
+ unsigned char value)
+{
+ int nResult = 0;
+
+ pTAS2559->client->addr = addr;
+ nResult = regmap_write(pTAS2559->mpRegmap, reg, value);
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s[0x%x] Error %d\n",
+ __func__, addr, nResult);
+
+ return nResult;
+}
+
+/*
+* tas2559_i2c_bulkwrite_device : write multiple bytes to device
+* platform dependent, need platform specific support
+*/
+static int tas2559_i2c_bulkwrite_device(struct tas2559_priv *pTAS2559,
+ unsigned char addr,
+ unsigned char reg,
+ unsigned char *pBuf,
+ unsigned int len)
+{
+ int nResult = 0;
+
+ pTAS2559->client->addr = addr;
+ nResult = regmap_bulk_write(pTAS2559->mpRegmap, reg, pBuf, len);
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s[0x%x] Error %d\n",
+ __func__, addr, nResult);
+
+ return nResult;
+}
+
+/*
+* tas2559_i2c_read_device : read single byte from device
+* platform dependent, need platform specific support
+*/
+static int tas2559_i2c_read_device(struct tas2559_priv *pTAS2559,
+ unsigned char addr,
+ unsigned char reg,
+ unsigned char *p_value)
+{
+ int nResult = 0;
+ unsigned int val = 0;
+
+ pTAS2559->client->addr = addr;
+ nResult = regmap_read(pTAS2559->mpRegmap, reg, &val);
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s[0x%x] Error %d\n",
+ __func__, addr, nResult);
+ else
+ *p_value = (unsigned char)val;
+
+ return nResult;
+}
+
+/*
+* tas2559_i2c_bulkread_device : read multiple bytes from device
+* platform dependent, need platform specific support
+*/
+static int tas2559_i2c_bulkread_device(struct tas2559_priv *pTAS2559,
+ unsigned char addr,
+ unsigned char reg,
+ unsigned char *p_value,
+ unsigned int len)
+{
+ int nResult = 0;
+
+ pTAS2559->client->addr = addr;
+ nResult = regmap_bulk_read(pTAS2559->mpRegmap, reg, p_value, len);
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s[0x%x] Error %d\n",
+ __func__, addr, nResult);
+
+ return nResult;
+}
+
+static int tas2559_i2c_update_bits(struct tas2559_priv *pTAS2559,
+ unsigned char addr,
+ unsigned char reg,
+ unsigned char mask,
+ unsigned char value)
+{
+ int nResult = 0;
+
+ pTAS2559->client->addr = addr;
+ nResult = regmap_update_bits(pTAS2559->mpRegmap, reg, mask, value);
+
+ if (nResult < 0)
+ dev_err(pTAS2559->dev, "%s[0x%x] Error %d\n",
+ __func__, addr, nResult);
+
+ return nResult;
+}
+
+/*
+* tas2559_change_book_page : switch to certain book and page
+* platform independent, don't change unless necessary
+*/
+static int tas2559_change_book_page(struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned char nBook,
+ unsigned char nPage)
+{
+ int nResult = 0;
+
+ if (chn & DevA) {
+ if (pTAS2559->mnDevACurrentBook == nBook) {
+ if (pTAS2559->mnDevACurrentPage != nPage) {
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_BOOKCTL_PAGE, nPage);
+
+ if (nResult >= 0)
+ pTAS2559->mnDevACurrentPage = nPage;
+ }
+ } else {
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_BOOKCTL_PAGE, 0);
+
+ if (nResult >= 0) {
+ pTAS2559->mnDevACurrentPage = 0;
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_BOOKCTL_REG, nBook);
+ pTAS2559->mnDevACurrentBook = nBook;
+
+ if (nPage != 0) {
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_BOOKCTL_PAGE, nPage);
+ pTAS2559->mnDevACurrentPage = nPage;
+ }
+ }
+ }
+ }
+
+ if (chn & DevB) {
+ if (pTAS2559->mnDevBCurrentBook == nBook) {
+ if (pTAS2559->mnDevBCurrentPage != nPage) {
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_BOOKCTL_PAGE, nPage);
+
+ if (nResult >= 0)
+ pTAS2559->mnDevBCurrentPage = nPage;
+ }
+ } else {
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_BOOKCTL_PAGE, 0);
+
+ if (nResult >= 0) {
+ pTAS2559->mnDevBCurrentPage = 0;
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_BOOKCTL_REG, nBook);
+ pTAS2559->mnDevBCurrentBook = nBook;
+
+ if (nPage != 0) {
+ tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_BOOKCTL_PAGE, nPage);
+ pTAS2559->mnDevBCurrentPage = nPage;
+ }
+ }
+ }
+ }
+
+ return nResult;
+}
+
+/*
+* tas2559_dev_read :
+* platform independent, don't change unless necessary
+*/
+static int tas2559_dev_read(struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned int nRegister,
+ unsigned int *pValue)
+{
+ int nResult = 0;
+ unsigned char Value = 0;
+
+ mutex_lock(&pTAS2559->dev_lock);
+
+ nResult = tas2559_change_book_page(pTAS2559, chn,
+ TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister));
+
+ if (nResult >= 0) {
+ if (chn == DevA)
+ nResult = tas2559_i2c_read_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_PAGE_REG(nRegister), &Value);
+ else
+ if (chn == DevB)
+ nResult = tas2559_i2c_read_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_PAGE_REG(nRegister), &Value);
+ else {
+ dev_err(pTAS2559->dev, "%s, read chn ERROR %d\n", __func__, chn);
+ nResult = -EINVAL;
+ }
+
+ if (nResult >= 0)
+ *pValue = Value;
+ }
+
+end:
+
+ mutex_unlock(&pTAS2559->dev_lock);
+ return nResult;
+}
+
+/*
+* tas2559_dev_write :
+* platform independent, don't change unless necessary
+*/
+static int tas2559_dev_write(struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned int nRegister,
+ unsigned int nValue)
+{
+ int nResult = 0;
+
+ mutex_lock(&pTAS2559->dev_lock);
+
+ nResult = tas2559_change_book_page(pTAS2559,
+ chn, TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister));
+
+ if (nResult >= 0) {
+ if (chn & DevA)
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_PAGE_REG(nRegister), nValue);
+
+ if (chn & DevB)
+ nResult = tas2559_i2c_write_device(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_PAGE_REG(nRegister), nValue);
+ }
+
+end:
+
+ mutex_unlock(&pTAS2559->dev_lock);
+ return nResult;
+}
+
+/*
+* tas2559_dev_bulk_read :
+* platform independent, don't change unless necessary
+*/
+static int tas2559_dev_bulk_read(struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned int nRegister,
+ unsigned char *pData,
+ unsigned int nLength)
+{
+ int nResult = 0;
+ unsigned char reg = 0;
+
+ mutex_lock(&pTAS2559->dev_lock);
+
+ nResult = tas2559_change_book_page(pTAS2559, chn,
+ TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister));
+
+ if (nResult >= 0) {
+ reg = TAS2559_PAGE_REG(nRegister);
+
+ if (chn == DevA)
+ nResult = tas2559_i2c_bulkread_device(pTAS2559,
+ pTAS2559->mnDevAAddr, reg, pData, nLength);
+ else
+ if (chn == DevB)
+ nResult = tas2559_i2c_bulkread_device(pTAS2559,
+ pTAS2559->mnDevBAddr, reg, pData, nLength);
+ else {
+ dev_err(pTAS2559->dev, "%s, chn ERROR %d\n", __func__, chn);
+ nResult = -EINVAL;
+ }
+ }
+
+end:
+
+ mutex_unlock(&pTAS2559->dev_lock);
+ return nResult;
+}
+
+/*
+* tas2559_dev_bulk_write :
+* platform independent, don't change unless necessary
+*/
+static int tas2559_dev_bulk_write(struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned int nRegister,
+ unsigned char *pData,
+ unsigned int nLength)
+{
+ int nResult = 0;
+ unsigned char reg = 0;
+
+ mutex_lock(&pTAS2559->dev_lock);
+
+ nResult = tas2559_change_book_page(pTAS2559, chn,
+ TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister));
+
+ if (nResult >= 0) {
+ reg = TAS2559_PAGE_REG(nRegister);
+
+ if (chn & DevA)
+ nResult = tas2559_i2c_bulkwrite_device(pTAS2559,
+ pTAS2559->mnDevAAddr, reg, pData, nLength);
+
+ if (chn & DevB)
+ nResult = tas2559_i2c_bulkwrite_device(pTAS2559,
+ pTAS2559->mnDevBAddr, reg, pData, nLength);
+ }
+
+end:
+
+ mutex_unlock(&pTAS2559->dev_lock);
+ return nResult;
+}
+
+/*
+* tas2559_dev_update_bits :
+* platform independent, don't change unless necessary
+*/
+static int tas2559_dev_update_bits(
+ struct tas2559_priv *pTAS2559,
+ enum channel chn,
+ unsigned int nRegister,
+ unsigned int nMask,
+ unsigned int nValue)
+{
+ int nResult = 0;
+
+ mutex_lock(&pTAS2559->dev_lock);
+
+ nResult = tas2559_change_book_page(pTAS2559,
+ chn, TAS2559_BOOK_ID(nRegister), TAS2559_PAGE_ID(nRegister));
+
+ if (nResult >= 0) {
+ if (chn & DevA)
+ nResult = tas2559_i2c_update_bits(pTAS2559,
+ pTAS2559->mnDevAAddr, TAS2559_PAGE_REG(nRegister), nMask, nValue);
+
+ if (chn & DevB)
+ nResult = tas2559_i2c_update_bits(pTAS2559,
+ pTAS2559->mnDevBAddr, TAS2559_PAGE_REG(nRegister), nMask, nValue);
+ }
+
+end:
+
+ mutex_unlock(&pTAS2559->dev_lock);
+ return nResult;
+}
+
+void tas2559_clearIRQ(struct tas2559_priv *pTAS2559)
+{
+ unsigned int nValue;
+ int nResult = 0;
+
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_FLAGS_1, &nValue);
+
+ if (nResult >= 0)
+ pTAS2559->read(pTAS2559, DevA, TAS2559_FLAGS_2, &nValue);
+
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_FLAGS_1, &nValue);
+
+ if (nResult >= 0)
+ pTAS2559->read(pTAS2559, DevB, TAS2560_FLAGS_2, &nValue);
+}
+
+void tas2559_enableIRQ(struct tas2559_priv *pTAS2559, enum channel chl, bool enable)
+{
+ static bool bDevAEnable;
+ static bool bDevBEnable;
+
+ if (enable) {
+ if (pTAS2559->mbIRQEnable)
+ return;
+
+ if (chl & DevA) {
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)) {
+ enable_irq(pTAS2559->mnDevAIRQ);
+ bDevAEnable = true;
+ }
+ }
+ if (chl & DevB) {
+ if (gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (pTAS2559->mnDevAGPIOIRQ == pTAS2559->mnDevBGPIOIRQ) {
+ if (!bDevAEnable) {
+ enable_irq(pTAS2559->mnDevBIRQ);
+ bDevBEnable = true;
+ } else
+ bDevBEnable = false;
+ } else {
+ enable_irq(pTAS2559->mnDevBIRQ);
+ bDevBEnable = true;
+ }
+ }
+ }
+
+ if (bDevAEnable || bDevBEnable) {
+ /* check after 10 ms */
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)
+ || gpio_is_valid(pTAS2559->mnDevBGPIOIRQ))
+ schedule_delayed_work(&pTAS2559->irq_work, msecs_to_jiffies(10));
+ }
+ pTAS2559->mbIRQEnable = true;
+ } else {
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)) {
+ if (bDevAEnable) {
+ disable_irq_nosync(pTAS2559->mnDevAIRQ);
+ bDevAEnable = false;
+ }
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (bDevBEnable) {
+ disable_irq_nosync(pTAS2559->mnDevBIRQ);
+ bDevBEnable = false;
+ }
+ }
+
+ pTAS2559->mbIRQEnable = false;
+ }
+}
+
+static void tas2559_hw_reset(struct tas2559_priv *pTAS2559)
+{
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIORST)) {
+ gpio_direction_output(pTAS2559->mnDevAGPIORST, 0);
+ msleep(5);
+ gpio_direction_output(pTAS2559->mnDevAGPIORST, 1);
+ msleep(2);
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevBGPIORST)) {
+ if (pTAS2559->mnDevAGPIORST != pTAS2559->mnDevBGPIORST) {
+ gpio_direction_output(pTAS2559->mnDevBGPIORST, 0);
+ msleep(5);
+ gpio_direction_output(pTAS2559->mnDevBGPIORST, 1);
+ msleep(2);
+ }
+ }
+
+ pTAS2559->mnDevACurrentBook = -1;
+ pTAS2559->mnDevACurrentPage = -1;
+ pTAS2559->mnDevBCurrentBook = -1;
+ pTAS2559->mnDevBCurrentPage = -1;
+
+ if (pTAS2559->mnErrCode)
+ dev_info(pTAS2559->dev, "%s, ErrCode=0x%x\n", __func__, pTAS2559->mnErrCode);
+
+ pTAS2559->mnErrCode = 0;
+}
+
+static void irq_work_routine(struct work_struct *work)
+{
+ struct tas2559_priv *pTAS2559 =
+ container_of(work, struct tas2559_priv, irq_work.work);
+ struct TConfiguration *pConfiguration;
+ unsigned int nDevLInt1Status = 0, nDevLInt2Status = 0;
+ unsigned int nDevRInt1Status = 0, nDevRInt2Status = 0;
+ int nCounter = 2;
+ int nResult = 0;
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_lock(&pTAS2559->codec_lock);
+#endif
+
+ if (pTAS2559->mbRuntimeSuspend) {
+ dev_info(pTAS2559->dev, "%s, Runtime Suspended\n", __func__);
+ goto end;
+ }
+
+ if(pTAS2559->mnErrCode & ERROR_FAILSAFE)
+ goto program;
+
+ if (!pTAS2559->mbPowerUp) {
+ dev_info(pTAS2559->dev, "%s, device not powered\n", __func__);
+ goto end;
+ }
+
+ if ((!pTAS2559->mpFirmware->mnConfigurations)
+ || (!pTAS2559->mpFirmware->mnPrograms)) {
+ dev_info(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ pConfiguration = &(pTAS2559->mpFirmware->mpConfigurations[pTAS2559->mnCurrentConfiguration]);
+
+ if (pConfiguration->mnDevices & DevA) {
+ nResult = tas2559_dev_read(pTAS2559, DevA, TAS2559_FLAGS_1, &nDevLInt1Status);
+
+ if (nResult >= 0)
+ nResult = tas2559_dev_read(pTAS2559, DevA, TAS2559_FLAGS_2, &nDevLInt2Status);
+ else
+ goto program;
+
+ if (((nDevLInt1Status & 0xfc) != 0) || ((nDevLInt2Status & 0x0c) != 0)) {
+ /* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */
+ dev_dbg(pTAS2559->dev, "IRQ critical Error DevA: 0x%x, 0x%x\n",
+ nDevLInt1Status, nDevLInt2Status);
+
+ if (nDevLInt1Status & 0x80) {
+ pTAS2559->mnErrCode |= ERROR_OVER_CURRENT;
+ dev_err(pTAS2559->dev, "DEVA SPK over current!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_OVER_CURRENT;
+
+ if (nDevLInt1Status & 0x40) {
+ pTAS2559->mnErrCode |= ERROR_UNDER_VOLTAGE;
+ dev_err(pTAS2559->dev, "DEVA SPK under voltage!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
+
+ if (nDevLInt1Status & 0x20) {
+ pTAS2559->mnErrCode |= ERROR_CLK_HALT;
+ dev_err(pTAS2559->dev, "DEVA clk halted!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_HALT;
+
+ if (nDevLInt1Status & 0x10) {
+ pTAS2559->mnErrCode |= ERROR_DIE_OVERTEMP;
+ dev_err(pTAS2559->dev, "DEVA die over temperature!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_DIE_OVERTEMP;
+
+ if (nDevLInt1Status & 0x08) {
+ pTAS2559->mnErrCode |= ERROR_BROWNOUT;
+ dev_err(pTAS2559->dev, "DEVA brownout!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_BROWNOUT;
+
+ if (nDevLInt1Status & 0x04) {
+ pTAS2559->mnErrCode |= ERROR_CLK_LOST;
+ dev_err(pTAS2559->dev, "DEVA clock lost!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_LOST;
+
+ if (nDevLInt2Status & 0x08) {
+ pTAS2559->mnErrCode |= ERROR_CLK_DET1;
+ dev_err(pTAS2559->dev, "DEVA clk detection 1!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_DET1;
+
+ if (nDevLInt2Status & 0x04) {
+ pTAS2559->mnErrCode |= ERROR_CLK_DET2;
+ dev_err(pTAS2559->dev, "DEVA clk detection 2!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_DET2;
+
+ goto program;
+ } else {
+ dev_dbg(pTAS2559->dev, "IRQ status DevA: 0x%x, 0x%x\n",
+ nDevLInt1Status, nDevLInt2Status);
+ nCounter = 2;
+
+ while (nCounter > 0) {
+ nResult = tas2559_dev_read(pTAS2559, DevA, TAS2559_POWER_UP_FLAG_REG, &nDevLInt1Status);
+
+ if (nResult < 0)
+ goto program;
+
+ if ((nDevLInt1Status & 0xc0) == 0xc0)
+ break;
+
+ nCounter--;
+
+ if (nCounter > 0) {
+ /* in case check pow status just after power on TAS2559 */
+ dev_dbg(pTAS2559->dev, "PowSts A: 0x%x, check again after 10ms\n",
+ nDevLInt1Status);
+ msleep(10);
+ }
+ }
+
+ if ((nDevLInt1Status & 0xc0) != 0xc0) {
+ dev_err(pTAS2559->dev, "%s, Critical DevA ERROR B[%d]_P[%d]_R[%d]= 0x%x\n",
+ __func__,
+ TAS2559_BOOK_ID(TAS2559_POWER_UP_FLAG_REG),
+ TAS2559_PAGE_ID(TAS2559_POWER_UP_FLAG_REG),
+ TAS2559_PAGE_REG(TAS2559_POWER_UP_FLAG_REG),
+ nDevLInt1Status);
+ pTAS2559->mnErrCode |= ERROR_CLASSD_PWR;
+ goto program;
+ }
+
+ pTAS2559->mnErrCode &= ~ERROR_CLASSD_PWR;
+ }
+ }
+
+ if (pConfiguration->mnDevices & DevB) {
+ nResult = tas2559_dev_read(pTAS2559, DevB, TAS2560_FLAGS_1, &nDevRInt1Status);
+
+ if (nResult >= 0)
+ nResult = tas2559_dev_read(pTAS2559, DevB, TAS2560_FLAGS_2, &nDevRInt2Status);
+ else
+ goto program;
+
+ if (((nDevRInt1Status & 0xfc) != 0) || ((nDevRInt2Status & 0xc0) != 0)) {
+ /* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */
+ dev_dbg(pTAS2559->dev, "IRQ critical Error DevB: 0x%x, 0x%x\n",
+ nDevRInt1Status, nDevRInt2Status);
+
+ if (nDevRInt1Status & 0x80) {
+ pTAS2559->mnErrCode |= ERROR_OVER_CURRENT;
+ dev_err(pTAS2559->dev, "DEVB SPK over current!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_OVER_CURRENT;
+
+ if (nDevRInt1Status & 0x40) {
+ pTAS2559->mnErrCode |= ERROR_UNDER_VOLTAGE;
+ dev_err(pTAS2559->dev, "DEVB SPK under voltage!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
+
+ if (nDevRInt1Status & 0x20) {
+ pTAS2559->mnErrCode |= ERROR_CLK_HALT;
+ dev_err(pTAS2559->dev, "DEVB clk halted!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_HALT;
+
+ if (nDevRInt1Status & 0x10) {
+ pTAS2559->mnErrCode |= ERROR_DIE_OVERTEMP;
+ dev_err(pTAS2559->dev, "DEVB die over temperature!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_DIE_OVERTEMP;
+
+ if (nDevRInt1Status & 0x08) {
+ pTAS2559->mnErrCode |= ERROR_BROWNOUT;
+ dev_err(pTAS2559->dev, "DEVB brownout!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_BROWNOUT;
+
+ if (nDevRInt1Status & 0x04) {
+ pTAS2559->mnErrCode |= ERROR_CLK_LOST;
+ dev_err(pTAS2559->dev, "DEVB clock lost!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_LOST;
+
+ if (nDevRInt2Status & 0x80) {
+ pTAS2559->mnErrCode |= ERROR_CLK_DET1;
+ dev_err(pTAS2559->dev, "DEVB clk detection 1!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_DET1;
+
+ if (nDevRInt2Status & 0x40) {
+ pTAS2559->mnErrCode |= ERROR_CLK_DET2;
+ dev_err(pTAS2559->dev, "DEVB clk detection 2!\n");
+ } else
+ pTAS2559->mnErrCode &= ~ERROR_CLK_DET2;
+
+ goto program;
+ } else {
+ dev_dbg(pTAS2559->dev, "IRQ status DevB: 0x%x, 0x%x\n",
+ nDevRInt1Status, nDevRInt2Status);
+ nCounter = 2;
+
+ while (nCounter > 0) {
+ nResult = tas2559_dev_read(pTAS2559, DevB, TAS2560_POWER_UP_FLAG_REG, &nDevRInt1Status);
+
+ if (nResult < 0)
+ goto program;
+
+ if ((nDevRInt1Status & 0xc0) == 0xc0)
+ break;
+
+ nCounter--;
+
+ if (nCounter > 0) {
+ /* in case check pow status just after power on TAS2560 */
+ dev_dbg(pTAS2559->dev, "PowSts B: 0x%x, check again after 10ms\n",
+ nDevRInt1Status);
+ msleep(10);
+ }
+ }
+
+ if ((nDevRInt1Status & 0xc0) != 0xc0) {
+ dev_err(pTAS2559->dev, "%s, Critical DevB ERROR B[%d]_P[%d]_R[%d]= 0x%x\n",
+ __func__,
+ TAS2559_BOOK_ID(TAS2560_POWER_UP_FLAG_REG),
+ TAS2559_PAGE_ID(TAS2560_POWER_UP_FLAG_REG),
+ TAS2559_PAGE_REG(TAS2560_POWER_UP_FLAG_REG),
+ nDevRInt1Status);
+ pTAS2559->mnErrCode |= ERROR_CLASSD_PWR;
+ goto program;
+ }
+
+ pTAS2559->mnErrCode &= ~ERROR_CLASSD_PWR;
+ }
+ }
+
+ goto end;
+
+program:
+ /* hardware reset and reload */
+ tas2559_set_program(pTAS2559, pTAS2559->mnCurrentProgram, pTAS2559->mnCurrentConfiguration);
+
+end:
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_unlock(&pTAS2559->codec_lock);
+#endif
+}
+
+static irqreturn_t tas2559_irq_handler(int irq, void *dev_id)
+{
+ struct tas2559_priv *pTAS2559 = (struct tas2559_priv *)dev_id;
+
+ tas2559_enableIRQ(pTAS2559, DevBoth, false);
+
+ /* get IRQ status after 100 ms */
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)
+ || gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (!delayed_work_pending(&pTAS2559->irq_work))
+ schedule_delayed_work(&pTAS2559->irq_work, msecs_to_jiffies(100));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void timer_work_routine(struct work_struct *work)
+{
+ struct tas2559_priv *pTAS2559 = container_of(work, struct tas2559_priv, mtimerwork);
+ int nResult, nTemp, nActTemp;
+ struct TProgram *pProgram;
+ static int nAvg;
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_lock(&pTAS2559->codec_lock);
+#endif
+
+ if (pTAS2559->mbRuntimeSuspend) {
+ dev_info(pTAS2559->dev, "%s, Runtime Suspended\n", __func__);
+ goto end;
+ }
+
+ if (!pTAS2559->mpFirmware->mnConfigurations) {
+ dev_info(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+
+ if (!pTAS2559->mbPowerUp
+ || (pProgram->mnAppMode != TAS2559_APP_TUNINGMODE)) {
+ dev_info(pTAS2559->dev, "%s, pass, Pow=%d, program=%s\n",
+ __func__, pTAS2559->mbPowerUp, pProgram->mpName);
+ goto end;
+ }
+
+ nResult = tas2559_get_die_temperature(pTAS2559, &nTemp);
+
+ if (nResult >= 0) {
+ nActTemp = (int)(nTemp >> 23);
+ dev_dbg(pTAS2559->dev, "Die=0x%x, degree=%d\n", nTemp, nActTemp);
+
+ if (!pTAS2559->mnDieTvReadCounter)
+ nAvg = 0;
+
+ pTAS2559->mnDieTvReadCounter++;
+ nAvg += nActTemp;
+
+ if (!(pTAS2559->mnDieTvReadCounter % LOW_TEMPERATURE_COUNTER)) {
+ nAvg /= LOW_TEMPERATURE_COUNTER;
+ dev_dbg(pTAS2559->dev, "check : avg=%d\n", nAvg);
+
+ if (nAvg < -6) {
+ /* if Die temperature is below -6 degree C */
+ if (pTAS2559->mnDevCurrentGain != LOW_TEMPERATURE_GAIN) {
+ nResult = tas2559_set_DAC_gain(pTAS2559, DevBoth, LOW_TEMPERATURE_GAIN);
+
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mnDevCurrentGain = LOW_TEMPERATURE_GAIN;
+ dev_dbg(pTAS2559->dev, "LOW Temp: set gain to %d\n", LOW_TEMPERATURE_GAIN);
+ }
+ } else if (nAvg > 5) {
+ /* if Die temperature is above 5 degree C */
+ if (pTAS2559->mnDevCurrentGain != pTAS2559->mnDevGain) {
+ nResult = tas2559_set_DAC_gain(pTAS2559, DevBoth, pTAS2559->mnDevGain);
+
+ if (nResult < 0)
+ goto end;
+
+ pTAS2559->mnDevCurrentGain = pTAS2559->mnDevGain;
+ dev_dbg(pTAS2559->dev, "LOW Temp: set gain to original\n");
+ }
+ }
+
+ nAvg = 0;
+ }
+
+ if (pTAS2559->mbPowerUp)
+ hrtimer_start(&pTAS2559->mtimer,
+ ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+
+end:
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_unlock(&pTAS2559->codec_lock);
+#endif
+}
+
+static enum hrtimer_restart temperature_timer_func(struct hrtimer *timer)
+{
+ struct tas2559_priv *pTAS2559 = container_of(timer, struct tas2559_priv, mtimer);
+
+ if (pTAS2559->mbPowerUp) {
+ schedule_work(&pTAS2559->mtimerwork);
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)
+ || gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (!delayed_work_pending(&pTAS2559->irq_work))
+ schedule_delayed_work(&pTAS2559->irq_work, msecs_to_jiffies(20));
+ }
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static int tas2559_runtime_suspend(struct tas2559_priv *pTAS2559)
+{
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+
+ pTAS2559->mbRuntimeSuspend = true;
+
+ if (hrtimer_active(&pTAS2559->mtimer)) {
+ dev_dbg(pTAS2559->dev, "cancel die temp timer\n");
+ hrtimer_cancel(&pTAS2559->mtimer);
+ }
+ if (work_pending(&pTAS2559->mtimerwork)) {
+ dev_dbg(pTAS2559->dev, "cancel timer work\n");
+ cancel_work_sync(&pTAS2559->mtimerwork);
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)
+ || gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (delayed_work_pending(&pTAS2559->irq_work)) {
+ dev_dbg(pTAS2559->dev, "cancel IRQ work\n");
+ cancel_delayed_work_sync(&pTAS2559->irq_work);
+ }
+ }
+
+ return 0;
+}
+
+static int tas2559_runtime_resume(struct tas2559_priv *pTAS2559)
+{
+ struct TProgram *pProgram;
+
+ dev_dbg(pTAS2559->dev, "%s\n", __func__);
+ if (!pTAS2559->mpFirmware->mpPrograms) {
+ dev_dbg(pTAS2559->dev, "%s, firmware not loaded\n", __func__);
+ goto end;
+ }
+
+ if (pTAS2559->mnCurrentProgram >= pTAS2559->mpFirmware->mnPrograms) {
+ dev_err(pTAS2559->dev, "%s, firmware corrupted\n", __func__);
+ goto end;
+ }
+
+ pProgram = &(pTAS2559->mpFirmware->mpPrograms[pTAS2559->mnCurrentProgram]);
+ if (pTAS2559->mbPowerUp && (pProgram->mnAppMode == TAS2559_APP_TUNINGMODE)) {
+ if (!hrtimer_active(&pTAS2559->mtimer)) {
+ dev_dbg(pTAS2559->dev, "%s, start Die Temp check timer\n", __func__);
+ pTAS2559->mnDieTvReadCounter = 0;
+ hrtimer_start(&pTAS2559->mtimer,
+ ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ }
+
+ pTAS2559->mbRuntimeSuspend = false;
+end:
+
+ return 0;
+}
+
+static bool tas2559_volatile(struct device *pDev, unsigned int nRegister)
+{
+ return true;
+}
+
+static bool tas2559_writeable(struct device *pDev, unsigned int nRegister)
+{
+ return true;
+}
+
+static const struct regmap_config tas2559_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tas2559_writeable,
+ .volatile_reg = tas2559_volatile,
+ .cache_type = REGCACHE_NONE,
+ .max_register = 128,
+};
+
+/*
+* tas2559_i2c_probe :
+* platform dependent
+* should implement hardware reset functionality
+*/
+static int tas2559_i2c_probe(struct i2c_client *pClient,
+ const struct i2c_device_id *pID)
+{
+ struct tas2559_priv *pTAS2559;
+ int nResult;
+ unsigned int nValue = 0;
+
+ dev_info(&pClient->dev, "%s enter\n", __func__);
+
+ pTAS2559 = devm_kzalloc(&pClient->dev, sizeof(struct tas2559_priv), GFP_KERNEL);
+
+ if (!pTAS2559) {
+ dev_err(&pClient->dev, " -ENOMEM\n");
+ nResult = -ENOMEM;
+ goto err;
+ }
+
+ pTAS2559->client = pClient;
+ pTAS2559->dev = &pClient->dev;
+ i2c_set_clientdata(pClient, pTAS2559);
+ dev_set_drvdata(&pClient->dev, pTAS2559);
+
+ pTAS2559->mpRegmap = devm_regmap_init_i2c(pClient, &tas2559_i2c_regmap);
+
+ if (IS_ERR(pTAS2559->mpRegmap)) {
+ nResult = PTR_ERR(pTAS2559->mpRegmap);
+ dev_err(&pClient->dev, "Failed to allocate register map: %d\n",
+ nResult);
+ goto err;
+ }
+
+ if (pClient->dev.of_node)
+ tas2559_parse_dt(&pClient->dev, pTAS2559);
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIORST)) {
+ nResult = gpio_request(pTAS2559->mnDevAGPIORST, "TAS2559-RESET");
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev, "%s: GPIO %d request error : %d\n",
+ __func__, pTAS2559->mnDevAGPIORST, nResult);
+ goto err;
+ }
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevBGPIORST)
+ && (pTAS2559->mnDevAGPIORST != pTAS2559->mnDevBGPIORST)) {
+ nResult = gpio_request(pTAS2559->mnDevBGPIORST, "TAS2560-RESET");
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev, "%s: GPIO %d request error : %d\n",
+ __func__, pTAS2559->mnDevBGPIORST, nResult);
+ goto err;
+ }
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIORST)
+ || gpio_is_valid(pTAS2559->mnDevBGPIORST))
+ tas2559_hw_reset(pTAS2559);
+
+ pTAS2559->read = tas2559_dev_read;
+ pTAS2559->write = tas2559_dev_write;
+ pTAS2559->bulk_read = tas2559_dev_bulk_read;
+ pTAS2559->bulk_write = tas2559_dev_bulk_write;
+ pTAS2559->update_bits = tas2559_dev_update_bits;
+ pTAS2559->enableIRQ = tas2559_enableIRQ;
+ pTAS2559->clearIRQ = tas2559_clearIRQ;
+ pTAS2559->set_config = tas2559_set_config;
+ pTAS2559->set_calibration = tas2559_set_calibration;
+ pTAS2559->hw_reset = tas2559_hw_reset;
+ pTAS2559->runtime_suspend = tas2559_runtime_suspend;
+ pTAS2559->runtime_resume = tas2559_runtime_resume;
+ pTAS2559->mnRestart = 0;
+
+ mutex_init(&pTAS2559->dev_lock);
+
+ /* Reset the chip */
+ nResult = tas2559_dev_write(pTAS2559, DevBoth, TAS2559_SW_RESET_REG, 1);
+ if (nResult < 0) {
+ dev_err(&pClient->dev, "I2c fail, %d\n", nResult);
+ goto err;
+ }
+ msleep(1);
+ nResult = pTAS2559->read(pTAS2559, DevA, TAS2559_REV_PGID_REG, &nValue);
+ pTAS2559->mnDevAPGID = nValue;
+ dev_info(&pClient->dev, "TAS2559 PGID=0x%x\n", nValue);
+ nResult = pTAS2559->read(pTAS2559, DevB, TAS2560_ID_REG, &nValue);
+ pTAS2559->mnDevBPGID = nValue;
+ dev_info(pTAS2559->dev, "TAS2560 PGID=0x%02x\n", nValue);
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)) {
+ nResult = gpio_request(pTAS2559->mnDevAGPIOIRQ, "TAS2559-IRQ");
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev,
+ "%s: GPIO %d request INT error\n",
+ __func__, pTAS2559->mnDevAGPIOIRQ);
+ goto err;
+ }
+
+ gpio_direction_input(pTAS2559->mnDevAGPIOIRQ);
+ pTAS2559->mnDevAIRQ = gpio_to_irq(pTAS2559->mnDevAGPIOIRQ);
+ dev_dbg(pTAS2559->dev, "irq = %d\n", pTAS2559->mnDevAIRQ);
+ nResult = request_threaded_irq(pTAS2559->mnDevAIRQ, tas2559_irq_handler,
+ NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ pClient->name, pTAS2559);
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev,
+ "request_irq failed, %d\n", nResult);
+ goto err;
+ }
+
+ disable_irq_nosync(pTAS2559->mnDevAIRQ);
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ if (pTAS2559->mnDevAGPIOIRQ != pTAS2559->mnDevBGPIOIRQ) {
+ nResult = gpio_request(pTAS2559->mnDevBGPIOIRQ, "TAS2560-IRQ");
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev,
+ "%s: GPIO %d request INT error\n",
+ __func__, pTAS2559->mnDevBGPIOIRQ);
+ goto err;
+ }
+
+ gpio_direction_input(pTAS2559->mnDevBGPIOIRQ);
+ pTAS2559->mnDevBIRQ = gpio_to_irq(pTAS2559->mnDevBGPIOIRQ);
+ dev_dbg(pTAS2559->dev, "irq = %d\n", pTAS2559->mnDevBIRQ);
+ nResult = request_threaded_irq(pTAS2559->mnDevBIRQ, tas2559_irq_handler,
+ NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ pClient->name, pTAS2559);
+
+ if (nResult < 0) {
+ dev_err(pTAS2559->dev,
+ "request_irq failed, %d\n", nResult);
+ goto err;
+ }
+
+ disable_irq_nosync(pTAS2559->mnDevBIRQ);
+ } else
+ pTAS2559->mnDevBIRQ = pTAS2559->mnDevAIRQ;
+ }
+
+ if (gpio_is_valid(pTAS2559->mnDevAGPIOIRQ)
+ || gpio_is_valid(pTAS2559->mnDevBGPIOIRQ)) {
+ INIT_DELAYED_WORK(&pTAS2559->irq_work, irq_work_routine);
+ }
+
+ pTAS2559->mpFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+
+ if (!pTAS2559->mpFirmware) {
+ dev_err(&pClient->dev, "mpFirmware ENOMEM\n");
+ nResult = -ENOMEM;
+ goto err;
+ }
+
+ pTAS2559->mpCalFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+
+ if (!pTAS2559->mpCalFirmware) {
+ dev_err(&pClient->dev, "mpCalFirmware ENOMEM\n");
+ nResult = -ENOMEM;
+ goto err;
+ }
+
+#ifdef CONFIG_TAS2559_CODEC
+ mutex_init(&pTAS2559->codec_lock);
+ tas2559_register_codec(pTAS2559);
+#endif
+
+ hrtimer_init(&pTAS2559->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ pTAS2559->mtimer.function = temperature_timer_func;
+ INIT_WORK(&pTAS2559->mtimerwork, timer_work_routine);
+
+ nResult = request_firmware_nowait(THIS_MODULE, 1, TAS2559_FW_NAME,
+ pTAS2559->dev, GFP_KERNEL, pTAS2559, tas2559_fw_ready);
+
+err:
+
+ return nResult;
+}
+
+static int tas2559_i2c_remove(struct i2c_client *pClient)
+{
+ struct tas2559_priv *pTAS2559 = i2c_get_clientdata(pClient);
+
+ dev_info(pTAS2559->dev, "%s\n", __func__);
+
+#ifdef CONFIG_TAS2559_CODEC
+ tas2559_deregister_codec(pTAS2559);
+ mutex_destroy(&pTAS2559->codec_lock);
+#endif
+
+ mutex_destroy(&pTAS2559->dev_lock);
+ return 0;
+}
+
+static const struct i2c_device_id tas2559_i2c_id[] = {
+ {"tas2559", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tas2559_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2559_of_match[] = {
+ {.compatible = "ti,tas2559"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tas2559_of_match);
+#endif
+
+static struct i2c_driver tas2559_i2c_driver = {
+ .driver = {
+ .name = "tas2559",
+ .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+ .of_match_table = of_match_ptr(tas2559_of_match),
+#endif
+ },
+ .probe = tas2559_i2c_probe,
+ .remove = tas2559_i2c_remove,
+ .id_table = tas2559_i2c_id,
+};
+
+module_i2c_driver(tas2559_i2c_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2559 Stereo I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2559.h b/sound/soc/codecs/tas2559.h
new file mode 100644
index 000000000000..009687f02885
--- /dev/null
+++ b/sound/soc/codecs/tas2559.h
@@ -0,0 +1,523 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2559.h
+**
+** Description:
+** definitions and data structures for TAS2559 Android Linux driver
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2559_H
+#define _TAS2559_H
+
+#include <linux/regmap.h>
+
+/* Page Control Register */
+#define TAS2559_PAGECTL_REG 0
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2559_BOOKCTL_PAGE 0
+#define TAS2559_BOOKCTL_REG 127
+
+#define TAS2559_REG(book, page, reg) ((((unsigned int)book * 256 * 128) + \
+ ((unsigned int)page * 128)) + reg)
+
+#define TAS2559_BOOK_ID(reg) ((unsigned char)(reg / (256 * 128)))
+#define TAS2559_PAGE_ID(reg) ((unsigned char)((reg % (256 * 128)) / 128))
+#define TAS2559_BOOK_REG(reg) ((unsigned char)(reg % (256 * 128)))
+#define TAS2559_PAGE_REG(reg) ((unsigned char)((reg % (256 * 128)) % 128))
+
+/* Book0, Page0 registers */
+#define TAS2559_SW_RESET_REG TAS2559_REG(0, 0, 1)
+
+#define TAS2559_REV_PGID_REG TAS2559_REG(0, 0, 3)
+#define TAS2559_PG_VERSION_1P0 0x00
+#define TAS2559_PG_VERSION_2P0 0x10
+#define TAS2559_PG_VERSION_2P1 0x20
+
+#define TAS2559_POWER_CTRL1_REG TAS2559_REG(0, 0, 4)
+#define TAS2559_POWER_CTRL2_REG TAS2559_REG(0, 0, 5)
+
+#define TAS2559_SPK_CTRL_REG TAS2559_REG(0, 0, 6)
+/* B0P0R6 - TAS2559_SPK_CTRL_REG */
+#define TAS2559_DAC_GAIN_MASK (0xf << 3)
+#define TAS2559_DAC_GAIN_SHIFT 0x03
+
+#define TAS2559_MUTE_REG TAS2559_REG(0, 0, 7)
+#define TAS2559_SNS_CTRL_REG TAS2559_REG(0, 0, 8)
+#define TAS2559_ADC_INPUT_SEL_REG TAS2559_REG(0, 0, 9)
+#define TAS2559_DBOOST_CTL_REG TAS2559_REG(0, 0, 10)
+#define TAS2559_NONAME11_REG TAS2559_REG(0, 0, 11)
+#define TAS2559_NONAME12_REG TAS2559_REG(0, 0, 12)
+#define TAS2559_NONAME13_REG TAS2559_REG(0, 0, 13)
+#define TAS2559_NONAME14_REG TAS2559_REG(0, 0, 14)
+#define TAS2559_NONAME15_REG TAS2559_REG(0, 0, 15)
+#define TAS2559_NONAME16_REG TAS2559_REG(0, 0, 16)
+#define TAS2559_NONAME17_REG TAS2559_REG(0, 0, 17)
+#define TAS2559_NONAME18_REG TAS2559_REG(0, 0, 18)
+#define TAS2559_SAR_SAMPLING_TIME_REG TAS2559_REG(0, 0, 19)
+#define TAS2559_SAR_ADC1_REG TAS2559_REG(0, 0, 20)
+#define TAS2559_SAR_ADC2_REG TAS2559_REG(0, 0, 21) /* B0_P0_R0x15 */
+#define TAS2559_CRC_CHECKSUM_REG TAS2559_REG(0, 0, 32) /* B0_P0_R0x20 */
+#define TAS2559_CRC_RESET_REG TAS2559_REG(0, 0, 33) /* B0_P0_R0x21 */
+#define TAS2559_DSP_MODE_SELECT_REG TAS2559_REG(0, 0, 34)
+#define TAS2559_SAFE_GUARD_REG TAS2559_REG(0, 0, 37) /* B0_P0_R0x25 */
+#define TAS2559_ASI_CTRL_REG TAS2559_REG(0, 0, 42)
+#define TAS2559_CLK_ERR_CTRL TAS2559_REG(0, 0, 44) /* B0_P0_R0x2c */
+#define TAS2559_CLK_ERR_CTRL2 TAS2559_REG(0, 0, 45) /* B0_P0_R0x2d*/
+#define TAS2559_CLK_ERR_CTRL3 TAS2559_REG(0, 0, 46) /* B0_P0_R0x2e*/
+#define TAS2559_DBOOST_CFG_REG TAS2559_REG(0, 0, 52)
+#define TAS2559_POWER_UP_FLAG_REG TAS2559_REG(0, 0, 100) /* B0_P0_R0x64 */
+#define TAS2559_FLAGS_1 TAS2559_REG(0, 0, 104) /* B0_P0_R0x68 */
+#define TAS2559_FLAGS_2 TAS2559_REG(0, 0, 108) /* B0_P0_R0x6c */
+
+/* Book0, Page1 registers */
+#define TAS2559_ASI1_DAC_FORMAT_REG TAS2559_REG(0, 1, 1)
+#define TAS2559_ASI1_ADC_FORMAT_REG TAS2559_REG(0, 1, 2)
+#define TAS2559_ASI1_OFFSET1_REG TAS2559_REG(0, 1, 3)
+#define TAS2559_ASI1_ADC_PATH_REG TAS2559_REG(0, 1, 7)
+#define TAS2559_ASI1_DAC_BCLK_REG TAS2559_REG(0, 1, 8)
+#define TAS2559_ASI1_DAC_WCLK_REG TAS2559_REG(0, 1, 9)
+#define TAS2559_ASI1_ADC_BCLK_REG TAS2559_REG(0, 1, 10)
+#define TAS2559_ASI1_ADC_WCLK_REG TAS2559_REG(0, 1, 11)
+#define TAS2559_ASI1_DIN_DOUT_MUX_REG TAS2559_REG(0, 1, 12)
+#define TAS2559_ASI1_BDIV_CLK_SEL_REG TAS2559_REG(0, 1, 13)
+#define TAS2559_ASI1_BDIV_CLK_RATIO_REG TAS2559_REG(0, 1, 14)
+#define TAS2559_ASI1_WDIV_CLK_RATIO_REG TAS2559_REG(0, 1, 15)
+#define TAS2559_ASI1_DAC_CLKOUT_REG TAS2559_REG(0, 1, 16)
+#define TAS2559_ASI1_ADC_CLKOUT_REG TAS2559_REG(0, 1, 17)
+
+#define TAS2559_ASI2_DAC_FORMAT_REG TAS2559_REG(0, 1, 21)
+#define TAS2559_ASI2_ADC_FORMAT_REG TAS2559_REG(0, 1, 22)
+#define TAS2559_ASI2_OFFSET1_REG TAS2559_REG(0, 1, 23)
+#define TAS2559_ASI2_ADC_PATH_REG TAS2559_REG(0, 1, 27)
+#define TAS2559_ASI2_DAC_BCLK_REG TAS2559_REG(0, 1, 28)
+#define TAS2559_ASI2_DAC_WCLK_REG TAS2559_REG(0, 1, 29)
+#define TAS2559_ASI2_ADC_BCLK_REG TAS2559_REG(0, 1, 30)
+#define TAS2559_ASI2_ADC_WCLK_REG TAS2559_REG(0, 1, 31)
+#define TAS2559_ASI2_DIN_DOUT_MUX_REG TAS2559_REG(0, 1, 32)
+#define TAS2559_ASI2_BDIV_CLK_SEL_REG TAS2559_REG(0, 1, 33)
+#define TAS2559_ASI2_BDIV_CLK_RATIO_REG TAS2559_REG(0, 1, 34)
+#define TAS2559_ASI2_WDIV_CLK_RATIO_REG TAS2559_REG(0, 1, 35)
+#define TAS2559_ASI2_DAC_CLKOUT_REG TAS2559_REG(0, 1, 36)
+#define TAS2559_ASI2_ADC_CLKOUT_REG TAS2559_REG(0, 1, 37)
+
+#define TAS2559_GPIO1_PIN_REG TAS2559_REG(0, 1, 61) /* B0_P1_R0x3d */
+#define TAS2559_GPIO2_PIN_REG TAS2559_REG(0, 1, 62) /* B0_P1_R0x3e */
+#define TAS2559_GPIO3_PIN_REG TAS2559_REG(0, 1, 63)
+#define TAS2559_GPIO4_PIN_REG TAS2559_REG(0, 1, 64)
+#define TAS2559_GPIO5_PIN_REG TAS2559_REG(0, 1, 65)
+#define TAS2559_GPIO6_PIN_REG TAS2559_REG(0, 1, 66)
+#define TAS2559_GPIO7_PIN_REG TAS2559_REG(0, 1, 67)
+#define TAS2559_GPIO8_PIN_REG TAS2559_REG(0, 1, 68)
+#define TAS2559_GPIO9_PIN_REG TAS2559_REG(0, 1, 69)
+#define TAS2559_GPIO10_PIN_REG TAS2559_REG(0, 1, 70)
+
+#define TAS2559_GPI_PIN_REG TAS2559_REG(0, 1, 77) /* B0_P1_R0x4d */
+#define TAS2559_GPIO_HIZ_CTRL1_REG TAS2559_REG(0, 1, 79)
+#define TAS2559_GPIO_HIZ_CTRL2_REG TAS2559_REG(0, 1, 80) /* B0_P1_R0x50 */
+#define TAS2559_GPIO_HIZ_CTRL3_REG TAS2559_REG(0, 1, 81)
+#define TAS2559_GPIO_HIZ_CTRL4_REG TAS2559_REG(0, 1, 82)
+#define TAS2559_GPIO_HIZ_CTRL5_REG TAS2559_REG(0, 1, 83)
+
+#define TAS2559_BIT_BANG_CTRL_REG TAS2559_REG(0, 1, 87)
+#define TAS2559_BIT_BANG_OUT1_REG TAS2559_REG(0, 1, 88)
+#define TAS2559_BIT_BANG_OUT2_REG TAS2559_REG(0, 1, 89)
+#define TAS2559_BIT_BANG_IN1_REG TAS2559_REG(0, 1, 90)
+#define TAS2559_BIT_BANG_IN2_REG TAS2559_REG(0, 1, 91)
+#define TAS2559_BIT_BANG_IN3_REG TAS2559_REG(0, 1, 92)
+
+#define TAS2559_PDM_IN_CLK_REG TAS2559_REG(0, 1, 94)
+#define TAS2559_PDM_IN_PIN_REG TAS2559_REG(0, 1, 95)
+
+#define TAS2559_ASIM_IFACE1_REG TAS2559_REG(0, 1, 98)
+#define TAS2559_ASIM_FORMAT_REG TAS2559_REG(0, 1, 99)
+#define TAS2559_ASIM_IFACE3_REG TAS2559_REG(0, 1, 100)
+#define TAS2559_ASIM_IFACE4_REG TAS2559_REG(0, 1, 101)
+#define TAS2559_ASIM_IFACE5_REG TAS2559_REG(0, 1, 102)
+#define TAS2559_ASIM_IFACE6_REG TAS2559_REG(0, 1, 103)
+#define TAS2559_ASIM_IFACE7_REG TAS2559_REG(0, 1, 104)
+#define TAS2559_ASIM_IFACE8_REG TAS2559_REG(0, 1, 105)
+#define TAS2559_CLK_HALT_REG TAS2559_REG(0, 1, 106) /* B0_P1_R0x6a */
+#define TAS2559_INT_GEN1_REG TAS2559_REG(0, 1, 108) /* B0_P1_R0x6c */
+#define TAS2559_INT_GEN2_REG TAS2559_REG(0, 1, 109) /* B0_P1_R0x6d */
+#define TAS2559_INT_GEN3_REG TAS2559_REG(0, 1, 110) /* B0_P1_R0x6e */
+#define TAS2559_INT_GEN4_REG TAS2559_REG(0, 1, 111) /* B0_P1_R0x6f */
+#define TAS2559_INT_MODE_REG TAS2559_REG(0, 1, 114) /* B0_P1_R0x72 */
+#define TAS2559_MAIN_CLKIN_REG TAS2559_REG(0, 1, 115)
+#define TAS2559_PLL_CLKIN_REG TAS2559_REG(0, 1, 116)
+#define TAS2559_CLKOUT_MUX_REG TAS2559_REG(0, 1, 117)
+#define TAS2559_CLKOUT_CDIV_REG TAS2559_REG(0, 1, 118)
+#define TAS2559_HACK_GP01_REG TAS2559_REG(0, 1, 122)
+
+#define TAS2559_SLEEPMODE_CTL_REG TAS2559_REG(0, 2, 7)
+#define TAS2559_HACK01_REG TAS2559_REG(0, 2, 10)
+
+#define TAS2559_ISENSE_THRESHOLD TAS2559_REG(0, 50, 104)
+#define TAS2559_BOOSTON_EFFICIENCY TAS2559_REG(0, 51, 16)
+#define TAS2559_BOOSTOFF_EFFICIENCY TAS2559_REG(0, 51, 20)
+#define TAS2559_BOOST_HEADROOM TAS2559_REG(0, 51, 24)
+#define TAS2559_THERMAL_FOLDBACK_REG TAS2559_REG(0, 51, 100)
+
+#define TAS2559_SA_CHL_CTRL_REG TAS2559_REG(0, 53, 20) /* B0_P0x35_R0x14 */
+#define TAS2559_VPRED_COMP_REG TAS2559_REG(0, 53, 24)
+#define TAS2559_SA_COEFF_SWAP_REG TAS2559_REG(0, 53, 44) /* B0_P0x35_R0x2c */
+
+#define TAS2559_TEST_MODE_REG TAS2559_REG(0, 253, 13)
+#define TAS2559_BROADCAST_REG TAS2559_REG(0, 253, 54)
+#define TAS2559_VBST_VOLT_REG TAS2559_REG(0, 253, 58)/* B0_P0xfd_R0x3a */
+#define TAS2559_CRYPTIC_REG TAS2559_REG(0, 253, 71)
+
+#define TAS2559_DAC_INTERPOL_REG TAS2559_REG(100, 0, 1)
+#define TAS2559_SOFT_MUTE_REG TAS2559_REG(100, 0, 7)
+#define TAS2559_PLL_P_VAL_REG TAS2559_REG(100, 0, 27)
+#define TAS2559_PLL_J_VAL_REG TAS2559_REG(100, 0, 28)
+#define TAS2559_PLL_D_VAL_MSB_REG TAS2559_REG(100, 0, 29)
+#define TAS2559_PLL_D_VAL_LSB_REG TAS2559_REG(100, 0, 30)
+#define TAS2559_CLK_MISC_REG TAS2559_REG(100, 0, 31)
+#define TAS2559_PLL_N_VAL_REG TAS2559_REG(100, 0, 32)
+#define TAS2559_DAC_MADC_VAL_REG TAS2559_REG(100, 0, 33)
+#define TAS2559_ISENSE_DIV_REG TAS2559_REG(100, 0, 42)
+#define TAS2559_RAMP_CLK_DIV_MSB_REG TAS2559_REG(100, 0, 43)
+#define TAS2559_RAMP_CLK_DIV_LSB_REG TAS2559_REG(100, 0, 44)
+#define TAS2559_VBOOST_CTL_REG TAS2559_REG(100, 0, 64) /* B0x64_P0x00_R0x40 */
+
+#define TAS2559_DIE_TEMP_REG TAS2559_REG(130, 2, 124) /* B0x82_P0x02_R0x7C */
+#define TAS2559_DEVA_CALI_R0_REG TAS2559_REG(140, 47, 40) /* B0x8c_P0x2f_R0x28 */
+#define TAS2559_DEVB_CALI_R0_REG TAS2559_REG(140, 54, 12) /* B0x8c_P0x36_R0x0c */
+
+/* Bits */
+/* B0P0R4 - TAS2559_POWER_CTRL1_REG */
+#define TAS2559_SW_SHUTDOWN (0x1 << 0)
+#define TAS2559_MADC_POWER_UP (0x1 << 3)
+#define TAS2559_MDAC_POWER_UP (0x1 << 4)
+#define TAS2559_NDIV_POWER_UP (0x1 << 5)
+#define TAS2559_PLL_POWER_UP (0x1 << 6)
+#define TAS2559_DSP_POWER_UP (0x1 << 7)
+
+/* B0P0R5 - TAS2559_POWER_CTRL2_REG */
+#define TAS2559_VSENSE_ENABLE (0x1 << 0)
+#define TAS2559_ISENSE_ENABLE (0x1 << 1)
+#define TAS2559_BOOST_ENABLE (0x1 << 5)
+#define TAS2559_CLASSD_ENABLE (0x1 << 7)
+
+/* B0P0R7 - TAS2559_MUTE_REG */
+#define TAS2559_CLASSD_MUTE (0x1 << 0)
+#define TAS2559_ISENSE_MUTE (0x1 << 1)
+
+/* B0P253R13 - TAS2559_TEST_MODE_REG */
+#define TAS2559_TEST_MODE_ENABLE (13)
+#define TAS2559_TEST_MODE_MASK (0xf << 0)
+
+/* B0P253R71 - TAS2559_CRYPTIC_REG */
+#define TAS2559_OSC_TRIM_CAP(x) ((x & 0x3f) << 0)
+#define TAS2559_DISABLE_ENCRYPTION (0x1 << 6)
+#define TAS2559_SL_COMP (0x1 << 7)
+
+/* B0P1R115/6 - TAS2559_MAIN/PLL_CLKIN_REG */
+#define TAS2559_XXX_CLKIN_GPIO1 (0)
+#define TAS2559_XXX_CLKIN_GPIO2 (1)
+#define TAS2559_XXX_CLKIN_GPIO3 (2)
+#define TAS2559_XXX_CLKIN_GPIO4 (3)
+#define TAS2559_XXX_CLKIN_GPIO5 (4)
+#define TAS2559_XXX_CLKIN_GPIO6 (5)
+#define TAS2559_XXX_CLKIN_GPIO7 (6)
+#define TAS2559_XXX_CLKIN_GPIO8 (7)
+#define TAS2559_XXX_CLKIN_GPIO9 (8)
+#define TAS2559_XXX_CLKIN_GPIO10 (9)
+#define TAS2559_XXX_CLKIN_GPI1 (12)
+#define TAS2559_XXX_CLKIN_GPI2 (13)
+#define TAS2559_XXX_CLKIN_GPI3 (14)
+#define TAS2559_NDIV_CLKIN_PLL (15)
+#define TAS2559_PLL_CLKIN_INT_OSC (15)
+
+#define TAS2559_MCLK_CLKIN_SRC_GPIO1 (0)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO2 (1)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO3 (2)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO4 (3)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO5 (4)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO6 (5)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO7 (6)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO8 (7)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO9 (8)
+#define TAS2559_MCLK_CLKIN_SRC_GPIO10 (9)
+#define TAS2559_MCLK_CLKIN_SRC_GPI1 (12)
+#define TAS2559_MCLK_CLKIN_SRC_GPI2 (13)
+#define TAS2559_MCLK_CLKIN_SRC_GPI3 (14)
+
+#define TAS2559_FORMAT_I2S (0x0 << 5)
+#define TAS2559_FORMAT_DSP (0x1 << 5)
+#define TAS2559_FORMAT_RIGHT_J (0x2 << 5)
+#define TAS2559_FORMAT_LEFT_J (0x3 << 5)
+#define TAS2559_FORMAT_MONO_PCM (0x4 << 5)
+#define TAS2559_FORMAT_MASK (0x7 << 5)
+
+#define TAS2559_WORDLENGTH_16BIT (0x0 << 3)
+#define TAS2559_WORDLENGTH_20BIT (0x1 << 3)
+#define TAS2559_WORDLENGTH_24BIT (0x2 << 3)
+#define TAS2559_WORDLENGTH_32BIT (0x3 << 3)
+#define TAS2559_WORDLENGTH_MASK TAS2559_WORDLENGTH_32BIT
+
+/* B100P0R7 - TAS2559_SOFT_MUTE_REG */
+#define TAS2559_PDM_SOFT_MUTE (0x1 << 0)
+#define TAS2559_VSENSE_SOFT_MUTE (0x1 << 1)
+#define TAS2559_ISENSE_SOFT_MUTE (0x1 << 2)
+#define TAS2559_CLASSD_SOFT_MUTE (0x1 << 3)
+
+/* B100P0R27 - TAS2559_PLL_P_VAL_REG */
+#define TAS2559_PLL_P_VAL_MASK (0x3f << 0)
+
+/* B100P0R28 - TAS2559_PLL_J_VAL_REG */
+#define TAS2559_PLL_J_VAL_MASK ((unsigned int) (0x7f << 0))
+#define TAS2559_PLL_J_VAL_MASKX 0x00
+
+/* B100P0R29-30 - TAS2559_PLL_D_VAL_MSB/LSB_REG */
+#define TAS2559_PLL_D_MSB_VAL(x) ((x >> 8) & 0x3f)
+#define TAS2559_PLL_D_LSB_VAL(x) (x & 0xff)
+
+/* B100P0R31 - TAS2559_CLK_MISC_REG */
+#define TAS2559_DSP_CLK_FROM_PLL (0x1 << 5)
+
+#define TAS2559_FW_NAME "tas2559_uCDSP.bin"
+
+#define CHANNEL_LEFT (0)
+#define CHANNEL_RIGHT (1)
+
+#define TAS2559_APP_ROM1MODE 0
+#define TAS2559_APP_ROM2MODE 1
+#define TAS2559_APP_TUNINGMODE 2
+#define TAS2559_APP_ROM1_96KHZ 3
+#define TAS2559_APP_ROM2_96KHZ 4
+#define TAS2559_APP_RAMMODE 5
+
+#define TAS2559_BOOST_OFF 0
+#define TAS2559_BOOST_DEVA 1
+#define TAS2559_BOOST_DEVB 2
+#define TAS2559_BOOST_BOTH 3
+
+#define TAS2559_AD_BD 0 /* DevA default, DevB default */
+#define TAS2559_AM_BM 1 /* DevA mute, DevB mute */
+#define TAS2559_AL_BR 2 /* DevA left channel, DevB right channel */
+#define TAS2559_AR_BL 3 /* DevA right channel, DevB left channel */
+#define TAS2559_AH_BH 4 /* DevA (L+R)/2, DevB (L+R)/2 */
+
+#define TAS2559_VBST_DEFAULT 0 /* firmware default */
+#define TAS2559_VBST_A_ON 1 /* DevA always 8.5V, DevB default */
+#define TAS2559_VBST_B_ON 2 /* DevA default, DevB always 8.5V */
+#define TAS2559_VBST_A_ON_B_ON (TAS2559_VBST_A_ON | TAS2559_VBST_B_ON) /* both DevA and DevB always 8.5V */
+#define TAS2559_VBST_NEED_DEFAULT 0xff /* need default value */
+
+#define TAS2559_VBST_8P5V 0 /* coresponding PPG 0dB */
+#define TAS2559_VBST_8P1V 1 /* coresponding PPG -1dB */
+#define TAS2559_VBST_7P6V 2 /* coresponding PPG -2dB */
+#define TAS2559_VBST_6P6V 3 /* coresponding PPG -3dB */
+#define TAS2559_VBST_5P6V 4 /* coresponding PPG -4dB */
+
+#define ERROR_NONE 0x00000000
+#define ERROR_PLL_ABSENT 0x00000001
+#define ERROR_DEVA_I2C_COMM 0x00000002
+#define ERROR_DEVB_I2C_COMM 0x00000004
+#define ERROR_PRAM_CRCCHK 0x00000008
+#define ERROR_YRAM_CRCCHK 0x00000010
+#define ERROR_CLK_DET2 0x00000020
+#define ERROR_CLK_DET1 0x00000040
+#define ERROR_CLK_LOST 0x00000080
+#define ERROR_BROWNOUT 0x00000100
+#define ERROR_DIE_OVERTEMP 0x00000200
+#define ERROR_CLK_HALT 0x00000400
+#define ERROR_UNDER_VOLTAGE 0x00000800
+#define ERROR_OVER_CURRENT 0x00001000
+#define ERROR_CLASSD_PWR 0x00002000
+#define ERROR_SAFE_GUARD 0x00004000
+#define ERROR_FAILSAFE 0x40000000
+
+#define LOW_TEMPERATURE_GAIN 6
+#define LOW_TEMPERATURE_COUNTER 12
+
+struct TBlock {
+ unsigned int mnType;
+ unsigned char mbPChkSumPresent;
+ unsigned char mnPChkSum;
+ unsigned char mbYChkSumPresent;
+ unsigned char mnYChkSum;
+ unsigned int mnCommands;
+ unsigned char *mpData;
+};
+
+struct TData {
+ char mpName[64];
+ char *mpDescription;
+ unsigned int mnBlocks;
+ struct TBlock *mpBlocks;
+};
+
+struct TProgram {
+ char mpName[64];
+ char *mpDescription;
+ unsigned char mnAppMode;
+ unsigned short mnBoost;
+ struct TData mData;
+};
+
+struct TPLL {
+ char mpName[64];
+ char *mpDescription;
+ struct TBlock mBlock;
+};
+
+struct TConfiguration {
+ char mpName[64];
+ char *mpDescription;
+ unsigned int mnDevices;
+ unsigned int mnProgram;
+ unsigned int mnPLL;
+ unsigned int mnSamplingRate;
+ unsigned char mnPLLSrc;
+ unsigned int mnPLLSrcRate;
+ struct TData mData;
+};
+
+struct TCalibration {
+ char mpName[64];
+ char *mpDescription;
+ unsigned int mnProgram;
+ unsigned int mnConfiguration;
+ struct TData mData;
+};
+
+struct TFirmware {
+ 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;
+ struct TPLL *mpPLLs;
+ unsigned int mnPrograms;
+ struct TProgram *mpPrograms;
+ unsigned int mnConfigurations;
+ struct TConfiguration *mpConfigurations;
+ unsigned int mnCalibrations;
+ struct TCalibration *mpCalibrations;
+};
+
+struct tas2559_register {
+ int book;
+ int page;
+ int reg;
+};
+
+enum channel {
+ DevA = 0x01,
+ DevB = 0x02,
+ DevBoth = (DevA | DevB),
+};
+
+struct tas2559_priv {
+ struct device *dev;
+ struct regmap *mpRegmap;
+ struct i2c_client *client;
+ struct mutex dev_lock;
+ struct TFirmware *mpFirmware;
+ struct TFirmware *mpCalFirmware;
+ unsigned int mnCurrentProgram;
+ unsigned int mnCurrentSampleRate;
+ unsigned int mnCurrentConfiguration;
+ unsigned int mnNewConfiguration;
+ unsigned int mnCurrentCalibration;
+ enum channel mnCurrentChannel;
+ unsigned int mnBitRate;
+ bool mbPowerUp;
+ bool mbLoadConfigurationPrePowerUp;
+ struct delayed_work irq_work;
+ unsigned int mnEchoRef;
+ bool mbYCRCEnable;
+ bool mbIRQEnable;
+ bool mbCalibrationLoaded;
+
+ /* parameters for TAS2559 */
+ int mnDevAPGID;
+ int mnDevAGPIORST;
+ int mnDevAGPIOIRQ;
+ int mnDevAIRQ;
+ unsigned char mnDevAAddr;
+ unsigned char mnDevAChl;
+ unsigned char mnDevACurrentBook;
+ unsigned char mnDevACurrentPage;
+
+ /* parameters for TAS2560 */
+ int mnDevBPGID;
+ int mnDevBGPIORST;
+ int mnDevBGPIOIRQ;
+ int mnDevBIRQ;
+ unsigned char mnDevBAddr;
+ unsigned char mnDevBChl;
+ unsigned char mnDevBLoad;
+ unsigned char mnDevBCurrentBook;
+ unsigned char mnDevBCurrentPage;
+
+ int (*read)(struct tas2559_priv *pTAS2559,
+ enum channel chn, unsigned int reg, unsigned int *pValue);
+ int (*write)(struct tas2559_priv *pTAS2559,
+ enum channel chn, unsigned int reg, unsigned int Value);
+ int (*bulk_read)(struct tas2559_priv *pTAS2559,
+ enum channel chn, unsigned int reg, unsigned char *pData, unsigned int len);
+ int (*bulk_write)(struct tas2559_priv *pTAS2559,
+ enum channel chn, unsigned int reg, unsigned char *pData, unsigned int len);
+ int (*update_bits)(struct tas2559_priv *pTAS2559,
+ enum channel chn, unsigned int reg, unsigned int mask, unsigned int value);
+ int (*set_config)(struct tas2559_priv *pTAS2559, int config);
+ int (*set_calibration)(struct tas2559_priv *pTAS2559, int calibration);
+ void (*clearIRQ)(struct tas2559_priv *pTAS2559);
+ void (*enableIRQ)(struct tas2559_priv *pTAS2559, enum channel chl, bool enable);
+ void (*hw_reset)(struct tas2559_priv *pTAS2559);
+ /* device is working, but system is suspended */
+ int (*runtime_suspend)(struct tas2559_priv *pTAS2559);
+ int (*runtime_resume)(struct tas2559_priv *pTAS2559);
+
+ unsigned int mnVBoostState;
+ bool mbLoadVBoostPrePowerUp;
+ unsigned int mnVBoostVoltage;
+ unsigned int mnVBoostNewState;
+ unsigned int mnVBoostDefaultCfg[6];
+
+ /* for low temperature check */
+ unsigned int mnDevGain;
+ unsigned int mnDevCurrentGain;
+ unsigned int mnDieTvReadCounter;
+ struct hrtimer mtimer;
+ struct work_struct mtimerwork;
+
+ unsigned int mnChannelState;
+ unsigned char mnDefaultChlData[16];
+
+ /* device is working, but system is suspended */
+ bool mbRuntimeSuspend;
+
+ unsigned int mnErrCode;
+
+ unsigned int mnRestart;
+ bool mbMute;
+#ifdef CONFIG_TAS2559_CODEC
+ struct mutex codec_lock;
+#endif
+};
+
+#endif /* _TAS2559_H */
diff --git a/sound/soc/codecs/tas2560.h b/sound/soc/codecs/tas2560.h
new file mode 100644
index 000000000000..2405f8a7bfae
--- /dev/null
+++ b/sound/soc/codecs/tas2560.h
@@ -0,0 +1,142 @@
+/*
+** =============================================================================
+** 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.
+**
+** File:
+** tas2560.h
+**
+** Description:
+** definitions and data structures for TAS2559 Android Linux driver
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2560_H
+#define _TAS2560_H
+
+/* Page Control Register */
+#define TAS2560_PAGECTL_REG 0
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2560_BOOKCTL_PAGE 0
+#define TAS2560_BOOKCTL_REG 127
+
+#define TAS2560_REG(book, page, reg) (((book * 256 * 128) + \
+ (page * 128)) + reg)
+
+#define TAS2560_BOOK_ID(reg) (reg / (256 * 128))
+#define TAS2560_PAGE_ID(reg) ((reg % (256 * 128)) / 128)
+#define TAS2560_BOOK_REG(reg) (reg % (256 * 128))
+#define TAS2560_PAGE_REG(reg) ((reg % (256 * 128)) % 128)
+
+/* Book0, Page0 registers */
+#define TAS2560_SW_RESET_REG TAS2560_REG(0, 0, 1)
+#define TAS2560_DEV_MODE_REG TAS2560_REG(0, 0, 2)
+#define TAS2560_SPK_CTRL_REG TAS2560_REG(0, 0, 4)
+#define TAS2560_MUTE_REG TAS2560_REG(0, 0, 7)
+#define TAS2560_PWR_REG TAS2560_REG(0, 0, 7)
+#define TAS2560_PWR_BIT_MASK (0x3 << 6)
+#define TAS2560_MUTE_MASK (0x7)
+
+#define TAS2560_SR_CTRL1 TAS2560_REG(0, 0, 8)
+#define TAS2560_LOAD TAS2560_REG(0, 0, 9)
+#define TAS2560_SR_CTRL2 TAS2560_REG(0, 0, 13) /*B0_P0_R0x0d*/
+#define TAS2560_SR_CTRL3 TAS2560_REG(0, 0, 14) /*B0_P0_R0x0e*/
+
+#define TAS2560_CLK_SEL TAS2560_REG(0, 0, 15)
+#define TAS2560_PLL_SRC_MASK (0xc0)
+#define TAS2560_PLL_CLKIN_BCLK (0)
+#define TAS2560_PLL_CLKIN_MCLK (1)
+#define TAS2560_PLL_CLKIN_PDMCLK (2)
+#define TAS2560_PLL_P_MASK (0x3f)
+
+#define TAS2560_SET_FREQ TAS2560_REG(0, 0, 16)
+#define TAS2560_PLL_J_MASK (0x7f)
+
+#define TAS2560_PLL_D_MSB TAS2560_REG(0, 0, 17) /*B0_P0_R0x11*/
+#define TAS2560_PLL_D_LSB TAS2560_REG(0, 0, 18) /*B0_P0_R0x12*/
+
+#define TAS2560_DAI_FMT TAS2560_REG(0, 0, 20) /* B0_P0_R0x14 */
+#define TAS2560_ASI_CTRL TAS2560_REG(0, 0, 21) /* B0_P0_R0x15 */
+#define TAS2560_ASI_OFFSET_1 TAS2560_REG(0, 0, 22) /*B0_P0_R0x16*/
+#define TAS2560_ASI_CFG_1 TAS2560_REG(0, 0, 24) /* B0_P0_R0x18 */
+#define TAS2560_DIRINV_MASK 0x3c
+#define TAS2560_BCLKINV (1 << 2)
+#define TAS2560_WCLKINV (1 << 3)
+#define TAS2560_BCLKDIR (1 << 4)
+#define TAS2560_WCLKDIR (1 << 5)
+
+#define TAS2560_CLK_ERR_CTRL TAS2560_REG(0, 0, 33) /* B0_P0_R0x21 */
+#define TAS2560_IRQ_PIN_REG TAS2560_REG(0, 0, 35) /* B0_P0_R0x23 */
+#define TAS2560_INT_MODE_REG TAS2560_REG(0, 0, 36) /* B0_P0_R0x24 */
+#define TAS2560_INT_GEN_REG TAS2560_REG(0, 0, 37) /* B0_P0_R0x25 */
+#define TAS2560_FLAGS_1 TAS2560_REG(0, 0, 38) /* B0_P0_R0x26 */
+#define TAS2560_FLAGS_2 TAS2560_REG(0, 0, 39) /* B0_P0_R0x27 */
+#define TAS2560_POWER_UP_FLAG_REG TAS2560_REG(0, 0, 42) /* B0_P0_R0x2a */
+
+#define TAS2560_DR_BOOST_REG_2 TAS2560_REG(0, 0, 60) /* B0_P0_R0x3c */
+#define TAS2560_DR_BOOST_REG_1 TAS2560_REG(0, 0, 73) /* B0_P0_R0x49 */
+#define TAS2560_VBOOST_CTL_REG TAS2560_REG(0, 0, 79) /* B0_P0_R0x4f */
+#define TAS2560_CLK_ERR_CTRL2 TAS2560_REG(0, 0, 80) /* B0_P0_R0x50 */
+#define TAS2560_SLEEPMODE_CTL_REG TAS2560_REG(0, 0, 84) /* B0_P0_R0x54 */
+#define TAS2560_ID_REG TAS2560_REG(0, 0, 125)
+#define TAS2560_CRC_CHK_REG TAS2560_REG(0, 0, 126) /* B0_P0_R0x7e */
+
+/* Book0, Page50 registers */
+#define TAS2560_HPF_CUTOFF_CTL1 TAS2560_REG(0, 50, 28) /* B0_P0x32_R0x1c */
+#define TAS2560_HPF_CUTOFF_CTL2 TAS2560_REG(0, 50, 32) /* B0_P0x32_R0x20 */
+#define TAS2560_HPF_CUTOFF_CTL3 TAS2560_REG(0, 50, 36) /* B0_P0x32_R0x24 */
+
+#define TAS2560_ISENSE_PATH_CTL1 TAS2560_REG(0, 50, 40) /* B0_P0x32_R0x28 */
+#define TAS2560_ISENSE_PATH_CTL2 TAS2560_REG(0, 50, 44) /* B0_P0x32_R0x2c */
+#define TAS2560_ISENSE_PATH_CTL3 TAS2560_REG(0, 50, 48) /* B0_P0x32_R0x30 */
+
+#define TAS2560_VLIMIT_THRESHOLD TAS2560_REG(0, 50, 60)
+#define TAS2560_IDLE_CHNL_DETECT TAS2560_REG(0, 50, 108) /* B0_P0x32_R0x6c */
+
+/* Book0, Page51 registers */
+#define TAS2560_BOOST_HEAD TAS2560_REG(0, 51, 24) /* B0_P0x33_R0x18 */
+#define TAS2560_BOOST_ON TAS2560_REG(0, 51, 16) /* B0_P0x33_R0x10 */
+#define TAS2560_BOOST_OFF TAS2560_REG(0, 51, 20) /* B0_P0x33_R0x14 */
+#define TAS2560_BOOST_TABLE_CTRL1 TAS2560_REG(0, 51, 32) /* B0_P0x33_R0x20 */
+#define TAS2560_BOOST_TABLE_CTRL2 TAS2560_REG(0, 51, 36) /* B0_P0x33_R0x24 */
+#define TAS2560_BOOST_TABLE_CTRL3 TAS2560_REG(0, 51, 40) /* B0_P0x33_R0x28 */
+#define TAS2560_BOOST_TABLE_CTRL4 TAS2560_REG(0, 51, 44) /* B0_P0x33_R0x2c */
+#define TAS2560_BOOST_TABLE_CTRL5 TAS2560_REG(0, 51, 48) /* B0_P0x33_R0x30 */
+#define TAS2560_BOOST_TABLE_CTRL6 TAS2560_REG(0, 51, 52) /* B0_P0x33_R0x34 */
+#define TAS2560_BOOST_TABLE_CTRL7 TAS2560_REG(0, 51, 56) /* B0_P0x33_R0x38 */
+#define TAS2560_BOOST_TABLE_CTRL8 TAS2560_REG(0, 51, 60) /* B0_P0x33_R0x3c */
+#define TAS2560_THERMAL_FOLDBACK TAS2560_REG(0, 51, 100) /* B0_P0x33_R0x64 */
+
+/* Book0, Page52 registers */
+#define TAS2560_VSENSE_DEL_CTL1 TAS2560_REG(0, 52, 52) /* B0_P0x34_R0x34 */
+#define TAS2560_VSENSE_DEL_CTL2 TAS2560_REG(0, 52, 56) /* B0_P0x34_R0x38 */
+#define TAS2560_VSENSE_DEL_CTL3 TAS2560_REG(0, 52, 60) /* B0_P0x34_R0x3c */
+#define TAS2560_VSENSE_DEL_CTL4 TAS2560_REG(0, 52, 64) /* B0_P0x34_R0x40 */
+#define TAS2560_VSENSE_DEL_CTL5 TAS2560_REG(0, 52, 68) /* B0_P0x34_R0x44 */
+
+#define TAS2560_VBST_VOLT_REG TAS2559_REG(0, 253, 54) /* B0_P0xfd_R0x36 */
+
+#define TAS2560_DATAFORMAT_I2S (0x0 << 2)
+#define TAS2560_DATAFORMAT_DSP (0x1 << 2)
+#define TAS2560_DATAFORMAT_RIGHT_J (0x2 << 2)
+#define TAS2560_DATAFORMAT_LEFT_J (0x3 << 2)
+
+#define TAS2560_DAI_FMT_MASK (0x7 << 2)
+#define LOAD_MASK 0x18
+
+#define TAS2560_YRAM_BOOK 0
+#define TAS2560_YRAM_START_PAGE 50
+#define TAS2560_YRAM_END_PAGE 52
+#define TAS2560_YRAM_START_REG 8
+#define TAS2560_YRAM_END_REG 127
+
+#endif