diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8020097d4e4c80..9ec0e2bb2265d5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -230,6 +230,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_STAC9766 imply SND_SOC_STI_SAS imply SND_SOC_TAS2552 + imply SND_SOC_TAS2557_STEREO imply SND_SOC_TAS2562 imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 @@ -1679,7 +1680,17 @@ config SND_SOC_STI_SAS config SND_SOC_TAS2552 tristate "Texas Instruments TAS2552 Mono Audio amplifier" depends on I2C - + +config SND_SOC_TAS2557_STEREO + tristate "Texas Instruments TAS2557 SmartAmp(R) Stereo" +if SND_SOC_TAS2557_STEREO +config TAS2557_REGMAP_STEREO + bool "Use of RegMap API" +config TAS2557_CODEC_STEREO + bool "Codec Driver support" +config TAS2557_MISC_STEREO + bool "Misc Driver support" +endif # SND_SOC_TAS2557_STEREO config SND_SOC_TAS2562 tristate "Texas Instruments TAS2562 Mono Audio amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5cdbae88e6e357..ea570f8c490f9c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -262,6 +262,11 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-sti-sas-objs := sti-sas.o +snd-soc-tas2557s-objs := tas2557_stereo/tas2557-core.o \ + tas2557_stereo/tas2557-regmap.o \ + tas2557_stereo/tas2557-codec.o \ + tas2557_stereo/tas2557-misc.o \ + tas2557_stereo/tiload.o snd-soc-tas5086-objs := tas5086.o snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o @@ -630,6 +635,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o +obj-$(CONFIG_SND_SOC_TAS2557_STEREO) += snd-soc-tas2557s.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-codec.c b/sound/soc/codecs/tas2557_stereo/tas2557-codec.c new file mode 100644 index 00000000000000..b61e85b81e87f9 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-codec.c @@ -0,0 +1,954 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-codec.c + * Description: ALSA SoC driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier + */ + +#ifdef CONFIG_TAS2557_CODEC_STEREO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2557-core.h" +#include "tas2557-codec.h" + +#define KCONTROL_CODEC + +static unsigned int tas2557_codec_read(struct snd_soc_component *pCodec, + unsigned int nRegister) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + int ret = 0; + unsigned int Value = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = pTAS2557->read(pTAS2557, + pTAS2557->mnCurrentChannel, nRegister, &Value); + if (ret < 0) + dev_err(pTAS2557->dev, "%s, %d, ERROR happen=%d\n", __func__, + __LINE__, ret); + else + ret = Value; + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_codec_write(struct snd_soc_component *pCodec, unsigned int nRegister, + unsigned int nValue) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = pTAS2557->write(pTAS2557, + pTAS2557->mnCurrentChannel, nRegister, nValue); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_codec_suspend(struct snd_soc_component *pCodec) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + pTAS2557->runtime_suspend(pTAS2557); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_codec_resume(struct snd_soc_component *pCodec) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + pTAS2557->runtime_resume(pTAS2557); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static const struct snd_soc_dapm_widget tas2557_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("Stereo ASI1", "Stereo ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Stereo ASI2", "Stereo ASI2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Stereo ASIM", "Stereo ASIM Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Stereo DAC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUT_DRV("Stereo ClassD", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Stereo PLL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Stereo NDivider", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("Stereo OUT"), + SND_SOC_DAPM_INPUT("Stereo IN") +}; + +static const struct snd_soc_dapm_route tas2557_audio_map[] = { + {"Stereo DAC", NULL, "Stereo ASI1"}, + {"Stereo DAC", NULL, "Stereo ASI2"}, + {"Stereo DAC", NULL, "Stereo ASIM"}, + {"Stereo ClassD", NULL, "Stereo DAC"}, + {"Stereo OUT", NULL, "Stereo ClassD"}, + {"Stereo DAC", NULL, "Stereo PLL"}, + {"Stereo DAC", NULL, "Stereo NDivider"}, + {"Stereo ASI1 Capture", NULL, "IN"}, +}; + +static int tas2557_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + return 0; +} + +static void tas2557_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); +} + +static int tas2557_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + tas2557_enable(pTAS2557, !mute); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_set_dai_sysclk(struct snd_soc_dai *pDAI, + int nClkID, unsigned int nFreqency, int nDir) +{ + struct snd_soc_component *pCodec = pDAI->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + + dev_dbg(pTAS2557->dev, "tas2557_set_dai_sysclk: freq = %u\n", nFreqency); + + return 0; +} + +static int tas2557_hw_params(struct snd_pcm_substream *pSubstream, + struct snd_pcm_hw_params *pParams, struct snd_soc_dai *pDAI) +{ + struct snd_soc_component *pCodec = pDAI->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + /* do bit rate setting during platform data */ + /* tas2557_set_bit_rate(pTAS2557, channel_both, snd_pcm_format_width(params_format(pParams))); */ + tas2557_set_sampling_rate(pTAS2557, params_rate(pParams)); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_set_dai_fmt(struct snd_soc_dai *pDAI, unsigned int nFormat) +{ + struct snd_soc_component *component = pDAI->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + return 0; +} + +static int tas2557_prepare(struct snd_pcm_substream *pSubstream, + struct snd_soc_dai *pDAI) +{ + struct snd_soc_component *component = pDAI->component; + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + return 0; +} + +static int tas2557_set_bias_level(struct snd_soc_component *pCodec, + enum snd_soc_bias_level eLevel) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + + dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, eLevel); + return 0; +} + +static int tas2557_codec_probe(struct snd_soc_component *pCodec) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + return 0; +} + +static void tas2557_codec_remove(struct snd_soc_component *pCodec) +{ + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(pCodec); + + dev_dbg(pTAS2557->dev, "%s\n", __func__); +} + +static int tas2557_power_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mbPowerUp; + dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_get = %d\n", + pTAS2557->mbPowerUp); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_power_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + int nPowerOn = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_put = %d\n", nPowerOn); + tas2557_enable(pTAS2557, (nPowerOn != 0)); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_fs_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int nFS = 48000; + + mutex_lock(&pTAS2557->codec_lock); + + if (pTAS2557->mpFirmware->mnConfigurations) + nFS = pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration].mnSamplingRate; + pValue->value.integer.value[0] = nFS; + dev_dbg(pTAS2557->dev, "tas2557_fs_get = %d\n", nFS); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_fs_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int ret = 0; + int nFS = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2557->codec_lock); + + dev_info(pTAS2557->dev, "tas2557_fs_put = %d\n", nFS); + ret = tas2557_set_sampling_rate(pTAS2557, nFS); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_DevA_Cali_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + bool ret = 0; + int prm_r0 = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_get_Cali_prm_r0(pTAS2557, channel_left, &prm_r0); + if (ret) + pValue->value.integer.value[0] = prm_r0; + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_DevB_Cali_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + bool ret = 0; + int prm_r0 = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_get_Cali_prm_r0(pTAS2557, channel_right, &prm_r0); + if (ret) + pValue->value.integer.value[0] = prm_r0; + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_program_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mnCurrentProgram; + dev_dbg(pTAS2557->dev, "tas2557_program_get = %d\n", + pTAS2557->mnCurrentProgram); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_program_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned int nProgram = pValue->value.integer.value[0]; + int ret = 0, nConfiguration = -1; + + mutex_lock(&pTAS2557->codec_lock); + + if (nProgram == pTAS2557->mnCurrentProgram) + nConfiguration = pTAS2557->mnCurrentConfiguration; + ret = tas2557_set_program(pTAS2557, nProgram, nConfiguration); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_configuration_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mnCurrentConfiguration; + dev_dbg(pTAS2557->dev, "tas2557_configuration_get = %d\n", + pTAS2557->mnCurrentConfiguration); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_configuration_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned int nConfiguration = pValue->value.integer.value[0]; + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + dev_info(pTAS2557->dev, "%s = %d\n", __func__, nConfiguration); + ret = tas2557_set_config(pTAS2557, nConfiguration); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_calibration_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mnCurrentCalibration; + dev_info(pTAS2557->dev, + "tas2557_calibration_get = %d\n", + pTAS2557->mnCurrentCalibration); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static int tas2557_calibration_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned int nCalibration = pValue->value.integer.value[0]; + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_set_calibration(pTAS2557, nCalibration); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_ldac_gain_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned char nGain = 0; + int ret = -1; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_get_DAC_gain(pTAS2557, channel_left, &nGain); + if (ret >= 0) + pValue->value.integer.value[0] = nGain; + dev_dbg(pTAS2557->dev, "%s, ret = %d, %d\n", __func__, ret, nGain); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_ldac_gain_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned int nGain = pValue->value.integer.value[0]; + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_set_DAC_gain(pTAS2557, channel_left, nGain); + + mutex_unlock(&pTAS2557->codec_lock); + return ret; +} + +static int tas2557_rdac_gain_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned char nGain = 0; + int ret = -1; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_get_DAC_gain(pTAS2557, channel_right, &nGain); + if (ret >= 0) + pValue->value.integer.value[0] = nGain; + dev_dbg(pTAS2557->dev, "%s, ret = %d, %d\n", __func__, ret, nGain); + + mutex_unlock(&pTAS2557->codec_lock); + + return ret; +} + +static int tas2557_rdac_gain_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + unsigned int nGain = pValue->value.integer.value[0]; + int ret = 0; + + mutex_lock(&pTAS2557->codec_lock); + + ret = tas2557_set_DAC_gain(pTAS2557, channel_right, nGain); + + mutex_unlock(&pTAS2557->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 tas2557_dsp_chl_setup_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mnChannelState; + + mutex_unlock(&pTAS2557->codec_lock); + + return 0; +} + +static int tas2557_dsp_chl_setup_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int channel_state = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2557->codec_lock); + + tas2557_SA_DevChnSetup(pTAS2557, channel_state); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static const char * const vboost_ctl_text[] = { + "default", + "Device(s) AlwaysOn" +}; + +static const struct soc_enum vboost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vboost_ctl_text), vboost_ctl_text), +}; + +static int tas2557_vboost_ctl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int nResult = 0, nVBoost = 0; + + mutex_lock(&pTAS2557->codec_lock); + + nResult = tas2557_get_VBoost(pTAS2557, &nVBoost); + if (nResult >= 0) + pValue->value.integer.value[0] = nVBoost; + + mutex_unlock(&pTAS2557->codec_lock); + + return 0; +} + +static int tas2557_vboost_ctl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int vboost_state = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2557->codec_lock); + + tas2557_set_VBoost(pTAS2557, vboost_state, pTAS2557->mbPowerUp); + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static const char * const vboost_volt_text[] = { + "8.6V", + "8.1V", + "7.6V", + "6.6V", + "5.6V" +}; + +static const struct soc_enum vboost_volt_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(vboost_volt_text), vboost_volt_text), +}; + +static int tas2557_vboost_volt_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int nVBstVolt = 0; + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s, VBoost volt %d\n", __func__, pTAS2557->mnVBoostVoltage); + switch (pTAS2557->mnVBoostVoltage) { + case TAS2557_VBST_8P5V: + nVBstVolt = 0; + break; + + case TAS2557_VBST_8P1V: + nVBstVolt = 1; + break; + + case TAS2557_VBST_7P6V: + nVBstVolt = 2; + break; + + case TAS2557_VBST_6P6V: + nVBstVolt = 3; + break; + + case TAS2557_VBST_5P6V: + nVBstVolt = 4; + break; + + default: + dev_err(pTAS2557->dev, "%s, error volt %d\n", __func__, pTAS2557->mnVBoostVoltage); + break; + } + + pValue->value.integer.value[0] = nVBstVolt; + + mutex_unlock(&pTAS2557->codec_lock); + + return 0; +} + +static int tas2557_vboost_volt_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int vbstvolt = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2557->codec_lock); + + dev_dbg(pTAS2557->dev, "%s, volt %d\n", __func__, vbstvolt); + switch (vbstvolt) { + case 0: + pTAS2557->mnVBoostVoltage = TAS2557_VBST_8P5V; + break; + + case 1: + pTAS2557->mnVBoostVoltage = TAS2557_VBST_8P1V; + break; + + case 2: + pTAS2557->mnVBoostVoltage = TAS2557_VBST_7P6V; + break; + + case 3: + pTAS2557->mnVBoostVoltage = TAS2557_VBST_6P6V; + break; + + case 4: + pTAS2557->mnVBoostVoltage = TAS2557_VBST_5P6V; + break; + + default: + dev_err(pTAS2557->dev, "%s, error volt %d\n", __func__, vbstvolt); + break; + } + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static const char * const echoref_ctl_text[] = {"left channel", "right channel", "both channel"}; +static const struct soc_enum echoref_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(echoref_ctl_text), echoref_ctl_text), +}; + +static int tas2557_echoref_ctl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + + mutex_lock(&pTAS2557->codec_lock); + + pValue->value.integer.value[0] = pTAS2557->mnEchoRef; + + mutex_unlock(&pTAS2557->codec_lock); + + return 0; +} + +static int tas2557_echoref_ctl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_component *component = snd_soc_kcontrol_component(pKcontrol); +#else + struct snd_soc_component *component = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2557_priv *pTAS2557 = snd_soc_component_get_drvdata(component); + int echoref = pValue->value.integer.value[0]&0x01; /* only take care of left/right channel switch */ + + mutex_lock(&pTAS2557->codec_lock); + + if (echoref != pTAS2557->mnEchoRef) { + pTAS2557->mnEchoRef = echoref; + tas2557_SA_ctl_echoRef(pTAS2557); + } + + mutex_unlock(&pTAS2557->codec_lock); + return 0; +} + +static const struct snd_kcontrol_new tas2557_snd_controls[] = { + SOC_SINGLE_EXT("Stereo LDAC Playback Volume", SND_SOC_NOPM, 0, 0x0f, 0, + tas2557_ldac_gain_get, tas2557_ldac_gain_put), + SOC_SINGLE_EXT("Stereo RDAC Playback Volume", SND_SOC_NOPM, 0, 0x0f, 0, + tas2557_rdac_gain_get, tas2557_rdac_gain_put), + SOC_SINGLE_EXT("Stereo PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0, + tas2557_power_ctrl_get, tas2557_power_ctrl_put), + SOC_SINGLE_EXT("Stereo Program", SND_SOC_NOPM, 0, 0x00FF, 0, + tas2557_program_get, tas2557_program_put), + SOC_SINGLE_EXT("Stereo Configuration", SND_SOC_NOPM, 0, 0x00FF, 0, + tas2557_configuration_get, tas2557_configuration_put), + SOC_SINGLE_EXT("Stereo FS", SND_SOC_NOPM, 8000, 48000, 0, + tas2557_fs_get, tas2557_fs_put), + SOC_SINGLE_EXT("Get DevA Cali_Re", SND_SOC_NOPM, 0, 0x7f000000, 0, + tas2557_DevA_Cali_get, NULL), + SOC_SINGLE_EXT("Get DevB Cali_Re", SND_SOC_NOPM, 0, 0x7f000000, 0, + tas2557_DevB_Cali_get, NULL), + SOC_SINGLE_EXT("Stereo Calibration", SND_SOC_NOPM, 0, 0x00FF, 0, + tas2557_calibration_get, tas2557_calibration_put), + SOC_ENUM_EXT("Stereo DSPChl Setup", chl_setup_enum[0], + tas2557_dsp_chl_setup_get, tas2557_dsp_chl_setup_put), + SOC_ENUM_EXT("VBoost Ctrl", vboost_ctl_enum[0], + tas2557_vboost_ctl_get, tas2557_vboost_ctl_put), + SOC_ENUM_EXT("VBoost Volt", vboost_volt_enum[0], + tas2557_vboost_volt_get, tas2557_vboost_volt_put), + SOC_ENUM_EXT("Stereo EchoRef Ctrl", echoref_ctl_enum[0], + tas2557_echoref_ctl_get, tas2557_echoref_ctl_put), +}; + +static struct snd_soc_component_driver soc_component_driver_tas2557 = { + .probe = tas2557_codec_probe, + .remove = tas2557_codec_remove, + .read = tas2557_codec_read, + .write = tas2557_codec_write, + .suspend = tas2557_codec_suspend, + .resume = tas2557_codec_resume, + .set_bias_level = tas2557_set_bias_level, + .controls = tas2557_snd_controls, + .num_controls = ARRAY_SIZE(tas2557_snd_controls), + .dapm_widgets = tas2557_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2557_dapm_widgets), + .dapm_routes = tas2557_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2557_audio_map), +}; + +static struct snd_soc_dai_ops tas2557_dai_ops = { + .startup = tas2557_startup, + .shutdown = tas2557_shutdown, + .mute_stream = tas2557_mute, + .hw_params = tas2557_hw_params, + .prepare = tas2557_prepare, + .set_sysclk = tas2557_set_dai_sysclk, + .set_fmt = tas2557_set_dai_fmt, +}; + +#define TAS2557_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static struct snd_soc_dai_driver tas2557_dai_driver[] = { + { + .name = "tas2557 Stereo ASI1", + .id = 0, + .playback = { + .stream_name = "Stereo ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2557_FORMATS, + }, + .capture = { + .stream_name = "Stereo ASI1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2557_FORMATS, + }, + .ops = &tas2557_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "tas2557 Stereo ASI2", + .id = 1, + .playback = { + .stream_name = "Stereo ASI2 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2557_FORMATS, + }, + .ops = &tas2557_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "tas2557 Stereo ASIM", + .id = 2, + .playback = { + .stream_name = "Stereo ASIM Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2557_FORMATS, + }, + .ops = &tas2557_dai_ops, + .symmetric_rate = 1, + }, +}; + +int tas2557_register_codec(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; + + dev_info(pTAS2557->dev, "%s, enter\n", __func__); + nResult = snd_soc_register_component(pTAS2557->dev, + &soc_component_driver_tas2557, + tas2557_dai_driver, ARRAY_SIZE(tas2557_dai_driver)); + return nResult; +} + +int tas2557_deregister_codec(struct tas2557_priv *pTAS2557) +{ + snd_soc_unregister_component(pTAS2557->dev); + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2557 ALSA SOC Smart Amplifier Stereo driver"); +MODULE_LICENSE("GPL v2"); +#endif diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-codec.h b/sound/soc/codecs/tas2557_stereo/tas2557-codec.h new file mode 100644 index 00000000000000..16d1093524f098 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-codec.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-codec.h + * Description: header file for tas2557-codec.c + */ + +#ifndef _TAS2557_CODEC_H +#define _TAS2557_CODEC_H + +#include "tas2557.h" + +int tas2557_register_codec(struct tas2557_priv *pTAS2557); +int tas2557_deregister_codec(struct tas2557_priv *pTAS2557); + +#endif /* _TAS2557_CODEC_H */ diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-core.c b/sound/soc/codecs/tas2557_stereo/tas2557-core.c new file mode 100644 index 00000000000000..8949d298b46fe9 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-core.c @@ -0,0 +1,2757 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-core.c + * Description: TAS2557 common functions for Android Linux + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2557.h" +#include "tas2557-core.h" + +#define PPC_DRIVER_CRCCHK 0x00000200 +#define PPC_DRIVER_CONFDEV 0x00000300 +#define PPC_DRIVER_MTPLLSRC 0x00000400 +#define PPC_DRIVER_CFGDEV_NONCRC 0x00000101 + +#define TAS2557_CAL_NAME "/persist/audio/tas2557_cal.bin" +#define RESTART_MAX 3 + +static int tas2557_load_calibration(struct tas2557_priv *pTAS2557, + char *pFileName); +static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData, + unsigned int nType); +static void tas2557_clear_firmware(struct TFirmware *pFirmware); +static int tas2557_load_block(struct tas2557_priv *pTAS2557, struct TBlock *pBlock); +static int tas2557_load_configuration(struct tas2557_priv *pTAS2557, + unsigned int nConfiguration, bool bLoadSame); + +#define TAS2557_UDELAY 0xFFFFFFFE +#define TAS2557_MDELAY 0xFFFFFFFD + +#define TAS2557_BLOCK_PLL 0x00 +#define TAS2557_BLOCK_PGM_ALL 0x0d +#define TAS2557_BLOCK_PGM_DEV_A 0x01 +#define TAS2557_BLOCK_PGM_DEV_B 0x08 +#define TAS2557_BLOCK_CFG_COEFF_DEV_A 0x03 +#define TAS2557_BLOCK_CFG_COEFF_DEV_B 0x0a +#define TAS2557_BLOCK_CFG_PRE_DEV_A 0x04 +#define TAS2557_BLOCK_CFG_PRE_DEV_B 0x0b +#define TAS2557_BLOCK_CFG_POST 0x05 +#define TAS2557_BLOCK_CFG_POST_POWER 0x06 + +static unsigned int p_tas2557_default_data[] = { + TAS2557_SAR_ADC2_REG, 0x05, /* enable SAR ADC */ + TAS2557_CLK_ERR_CTRL2, 0x21, /*clk1:clock hysteresis, 0.34ms; clock halt, 22ms*/ + TAS2557_CLK_ERR_CTRL3, 0x21, /*clk2: rampDown 15dB/us, clock hysteresis, 10.66us; clock halt, 22ms */ + TAS2557_SAFE_GUARD_REG, TAS2557_SAFE_GUARD_PATTERN, /* safe guard */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2557_irq_config[] = { + TAS2557_CLK_HALT_REG, 0x71, /* enable clk halt detect2 interrupt */ + TAS2557_INT_GEN1_REG, 0x11, /* enable spk OC and OV */ + TAS2557_INT_GEN2_REG, 0x11, /* enable clk err1 and die OT */ + TAS2557_INT_GEN3_REG, 0x11, /* enable clk err2 and brownout */ + TAS2557_INT_GEN4_REG, 0x01, /* disable SAR, enable clk halt */ + TAS2557_GPIO4_PIN_REG, 0x07, /* set GPIO4 as int1, default */ + TAS2557_INT_MODE_REG, 0x80, /* active high until INT_STICKY_1 and INT_STICKY_2 are read to be cleared. */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2557_startup_data[] = { + TAS2557_GPI_PIN_REG, 0x15, /* enable DIN, MCLK, CCI */ + TAS2557_GPIO1_PIN_REG, 0x01, /* enable BCLK */ + TAS2557_GPIO2_PIN_REG, 0x01, /* enable WCLK */ + TAS2557_POWER_CTRL2_REG, 0xA0, /* Class-D, Boost power up */ + TAS2557_POWER_CTRL2_REG, 0xA3, /* Class-D, Boost, IV sense power up */ + TAS2557_POWER_CTRL1_REG, 0xF8, /* PLL, DSP, clock dividers power up */ + TAS2557_UDELAY, 2000, /* delay */ + TAS2557_CLK_ERR_CTRL, 0x2b, /* enable clock error detection */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2557_unmute_data[] = { + TAS2557_MUTE_REG, 0x00, /* unmute */ + TAS2557_SOFT_MUTE_REG, 0x00, /* soft unmute */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2557_shutdown_data[] = { + TAS2557_CLK_ERR_CTRL, 0x00, /* disable clock error detection */ + TAS2557_SOFT_MUTE_REG, 0x01, /* soft mute */ + TAS2557_UDELAY, 10000, /* delay 10ms */ + TAS2557_MUTE_REG, 0x03, /* mute */ + TAS2557_POWER_CTRL1_REG, 0x60, /* DSP power down */ + TAS2557_UDELAY, 2000, /* delay 2ms */ + TAS2557_POWER_CTRL2_REG, 0x00, /* Class-D, Boost power down */ + TAS2557_POWER_CTRL1_REG, 0x00, /* all power down */ + TAS2557_GPIO1_PIN_REG, 0x00, /* disable BCLK */ + TAS2557_GPIO2_PIN_REG, 0x00, /* disable WCLK */ + TAS2557_GPI_PIN_REG, 0x00, /* disable DIN, MCLK, CCI */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static int tas2557_dev_load_data(struct tas2557_priv *pTAS2557, + enum channel chl, unsigned int *pData) +{ + int ret = 0; + unsigned int n = 0; + unsigned int nRegister; + unsigned int nData; + + do { + nRegister = pData[n * 2]; + nData = pData[n * 2 + 1]; + if (nRegister == TAS2557_UDELAY) + udelay(nData); + else if (nRegister != 0xFFFFFFFF) { + ret = pTAS2557->write(pTAS2557, chl, nRegister, nData); + if (ret < 0) { + dev_err(pTAS2557->dev, "%s, write chl[%d],nReg=[0x%x],Data[0x%x]\n", + __func__, chl, nRegister, nData); + break; + } + } + n++; + } while (nRegister != 0xFFFFFFFF); + return ret; +} + +int tas2557_configIRQ(struct tas2557_priv *pTAS2557) +{ + return tas2557_dev_load_data(pTAS2557, channel_both, p_tas2557_irq_config); +} + +static int tas2557_enter_broadcast_mode(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; + + nResult = pTAS2557->write(pTAS2557, channel_both, TAS2557_TEST_MODE_REG, 0x0d); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_BROADCAST_REG, (pTAS2557->mnLBroadcastSet & 0x1f) | 0x80); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_BROADCAST_REG, (pTAS2557->mnRBroadcastSet & 0x1f) | 0x80); + if (nResult < 0) + goto end; + +end: + + return nResult; +} + +static int tas2557_exit_broadcast_mode(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; + + nResult = pTAS2557->write(pTAS2557, channel_broadcast, TAS2557_TEST_MODE_REG, 0x0d); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_broadcast, TAS2557_BROADCAST_REG, 0x01); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_both, TAS2557_TEST_MODE_REG, 0x0d); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_BROADCAST_REG, pTAS2557->mnLBroadcastSet & 0x1f); + if (nResult < 0) + goto end; + + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_BROADCAST_REG, pTAS2557->mnRBroadcastSet & 0x1f); + if (nResult < 0) + goto end; + +end: + + return nResult; +} + +/* + * for PG2.1 Dual Mono + */ +int tas2557_SA_DevChnSetup(struct tas2557_priv *pTAS2557, 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 *pDevABuf, *pDevBBuf; + + dev_dbg(pTAS2557->dev, "%s, mode %d\n", __func__, mode); + if ((pTAS2557->mpFirmware->mnPrograms == 0) + || (pTAS2557->mpFirmware->mnConfigurations == 0)) { + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + if (pProgram->mnAppMode != TAS2557_APP_TUNINGMODE) { + dev_err(pTAS2557->dev, "%s, not tuning mode\n", __func__); + goto end; + } + + if (pTAS2557->mnLPGID != TAS2557_PG_VERSION_2P1) { + dev_err(pTAS2557->dev, "%s, currently we only support PG2.1\n", __func__); + goto end; + } + + if (pTAS2557->mbLoadConfigurationPrePowerUp) { + dev_dbg(pTAS2557->dev, "%s, setup channel after coeff update\n", __func__); + pTAS2557->mnChannelState = mode; + goto end; + } + + switch (mode) { + case TAS2557_DM_AD_BD: + pDevABuf = pTAS2557->mnDevAChlData; + pDevBBuf = pTAS2557->mnDevBChlData; + break; + + case TAS2557_DM_AM_BM: + pDevABuf = buf_mute; + pDevBBuf = buf_mute; + break; + + case TAS2557_DM_AL_BR: + pDevABuf = buf_DevA_Left_DevB_Right; + pDevBBuf = buf_DevA_Left_DevB_Right; + break; + + case TAS2557_DM_AR_BL: + pDevABuf = buf_DevA_Right_DevB_Left; + pDevBBuf = buf_DevA_Right_DevB_Left; + break; + + case TAS2557_DM_AH_BH: + pDevABuf = buf_DevA_MonoMix_DevB_MonoMix; + pDevBBuf = buf_DevA_MonoMix_DevB_MonoMix; + break; + default: + break; + } + + if (pDevABuf && pDevBBuf) { + nResult = pTAS2557->bulk_write(pTAS2557, channel_left, TAS2557_SA_PG2P1_CHL_CTRL_REG, pDevABuf, 16); + if (nResult < 0) + goto end; + nResult = pTAS2557->bulk_write(pTAS2557, channel_right, TAS2557_SA_PG2P1_CHL_CTRL_REG, pDevBBuf, 16); + if (nResult < 0) + goto end; + pTAS2557->mnChannelState = mode; + } + +end: + + return nResult; +} + +int tas2557_SA_ctl_echoRef(struct tas2557_priv *pTAS2557) +{ + int ret = 0; + + if (pTAS2557->mnEchoRef == echoref_left) { + /* both TAS2557 can be configured as I2S slave mode (I2S standard) */ + /* disable right channel DOUT */ + ret = pTAS2557->write(pTAS2557, channel_right, TAS2557_GPIO3_PIN_REG, 0x00); + if (ret < 0) + goto end; + ret = pTAS2557->write(pTAS2557, channel_left, TAS2557_GPIO3_PIN_REG, 0x10); + } else if (pTAS2557->mnEchoRef == echoref_right) { + /* both TAS2557 can be configured as I2S slave mode (I2S standard) */ + /* disable left channel DOUT */ + ret = pTAS2557->write(pTAS2557, channel_left, TAS2557_GPIO3_PIN_REG, 0x00); + if (ret < 0) + goto end; + ret = pTAS2557->write(pTAS2557, channel_right, TAS2557_GPIO3_PIN_REG, 0x10); + } else if (pTAS2557->mnEchoRef == echoref_both) { + /* both TAS2557 can be configured as I2S slave mode (DSP mode) */ + /* BCLK = WCLK * Bits * 2 (echo reference + excursion) * 2 (left channel + right channel) */ + /* set TAS2557 to DSP mode and DOUT tri-state */ + ret = pTAS2557->update_bits(pTAS2557, channel_both, TAS2557_ASI1_DAC_FORMAT_REG, 0xe1, 0x21); + if (ret < 0) + goto end; + /* set TAS2557 BCLK and WCLK inverted */ + ret = pTAS2557->write(pTAS2557, channel_both, TAS2557_ASI1_DAC_BCLK_REG, 0x02); + ret = pTAS2557->write(pTAS2557, channel_both, TAS2557_ASI1_DAC_WCLK_REG, 0x0a); + /* left channel offset = 1 */ + ret = pTAS2557->write(pTAS2557, channel_left, TAS2557_ASI1_OFFSET1_REG, 0x01); + /* right channel offset = 1 + Bits*2 */ + ret = pTAS2557->write(pTAS2557, channel_right, TAS2557_ASI1_OFFSET1_REG, 0x01 + pTAS2557->mnI2SBits * 2); + } + +end: + + return ret; +} + +int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, enum channel chn, unsigned int nBitRate) +{ + int ret = 0, n = -1; + + dev_dbg(pTAS2557->dev, "tas2557_set_bit_rate: nBitRate = %d\n", nBitRate); + + switch (nBitRate) { + case 16: + n = 0; + break; + case 20: + n = 1; + break; + case 24: + n = 2; + break; + case 32: + n = 3; + break; + } + + if (n >= 0) + ret = pTAS2557->update_bits(pTAS2557, chn, + TAS2557_ASI1_DAC_FORMAT_REG, 0x18, n<<3); + return ret; +} + +int tas2557_get_bit_rate(struct tas2557_priv *pTAS2557, + enum channel chn, unsigned char *pBitRate) +{ + int ret = 0; + unsigned int nValue = 0; + unsigned char bitRate; + + ret = pTAS2557->read(pTAS2557, chn, + TAS2557_ASI1_DAC_FORMAT_REG, &nValue); + if (ret >= 0) { + bitRate = (nValue&0x18)>>3; + if (bitRate == 0) + bitRate = 16; + else if (bitRate == 1) + bitRate = 20; + else if (bitRate == 2) + bitRate = 24; + else if (bitRate == 3) + bitRate = 32; + *pBitRate = bitRate; + } else { + dev_err(pTAS2557->dev, "read left channel error %d\n", ret); + } + return ret; +} + +int tas2557_get_DAC_gain(struct tas2557_priv *pTAS2557, enum channel chl, unsigned char *pnGain) +{ + int ret = 0; + unsigned int nValue = 0; + + ret = pTAS2557->read(pTAS2557, chl, + TAS2557_SPK_CTRL_REG, &nValue); + if (ret >= 0) + *pnGain = ((nValue&TAS2557_DAC_GAIN_MASK)>>TAS2557_DAC_GAIN_SHIFT); + + return ret; +} + +int tas2557_set_DAC_gain(struct tas2557_priv *pTAS2557, enum channel chl, unsigned int nGain) +{ + int ret = 0; + + ret = pTAS2557->update_bits(pTAS2557, chl, TAS2557_SPK_CTRL_REG, TAS2557_DAC_GAIN_MASK, + (nGain<mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + if (!pTAS2557->mbPowerUp) { + dev_err(pTAS2557->dev, "%s, device not powered on\n", __func__); + goto end; + } + + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + if (pConfiguration->mnDevices & channel_left) + chl = channel_left; + else if (pConfiguration->mnDevices & channel_right) + chl = channel_right; + else { + dev_err(pTAS2557->dev, "%s, firmware error %d\n", __func__, pConfiguration->mnDevices); + goto end; + } + + nResult = pTAS2557->bulk_read(pTAS2557, chl, TAS2557_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 tas2557_update_VBstVolt(struct tas2557_priv *pTAS2557, enum channel chn) +{ + int nResult = 0; + int nVBstVoltSet = -1; + + switch (pTAS2557->mnVBoostVoltage) { + case TAS2557_VBST_8P5V: + nVBstVoltSet = 6; + dev_warn(pTAS2557->dev, "%s, PPG of this snapshot should be 0dB\n", __func__); + break; + + case TAS2557_VBST_8P1V: + nVBstVoltSet = 5; + dev_warn(pTAS2557->dev, "%s, PPG of this snapshot should be -1dB\n", __func__); + break; + + case TAS2557_VBST_7P6V: + nVBstVoltSet = 4; + dev_warn(pTAS2557->dev, "%s, PPG of this snapshot should be -2dB\n", __func__); + break; + + case TAS2557_VBST_6P6V: + nVBstVoltSet = 2; + dev_warn(pTAS2557->dev, "%s, PPG of this snapshot should be -3dB\n", __func__); + break; + + case TAS2557_VBST_5P6V: + nVBstVoltSet = 0; + dev_warn(pTAS2557->dev, "%s, PPG of this snapshot should be -4dB\n", __func__); + break; + + default: + dev_err(pTAS2557->dev, "%s, error volt %d\n", __func__, pTAS2557->mnVBoostVoltage); + break; + } + + if (nVBstVoltSet >= 0) { + if (chn & channel_left) + nResult = pTAS2557->update_bits(pTAS2557, channel_left, TAS2557_VBST_VOLT_REG, 0xe0, (nVBstVoltSet << 5)); + if (chn & channel_right) + nResult = pTAS2557->update_bits(pTAS2557, channel_right, TAS2557_VBST_VOLT_REG, 0xe0, (nVBstVoltSet << 5)); + dev_dbg(pTAS2557->dev, "%s, set vbst voltage (%d channel) 0x%x\n", __func__, chn, (nVBstVoltSet << 5)); + } + + return nResult; +} + +int tas2557_get_VBoost(struct tas2557_priv *pTAS2557, int *pVBoost) +{ + int nResult = 0; + + dev_dbg(pTAS2557->dev, "%s, VBoost state %d\n", __func__, pTAS2557->mnVBoostState); + switch (pTAS2557->mnVBoostState) { + case TAS2557_VBST_NEED_DEFAULT: + case TAS2557_VBST_DEFAULT: + *pVBoost = 0; + break; + + case TAS2557_VBST_A_ON: + case TAS2557_VBST_B_ON: + case TAS2557_VBST_A_ON_B_ON: + *pVBoost = 1; + break; + default: + dev_err(pTAS2557->dev, "%s, error state %d\n", __func__, pTAS2557->mnVBoostState); + break; + } + + return nResult; +} + +int tas2557_set_VBoost(struct tas2557_priv *pTAS2557, int vboost, bool bPowerOn) +{ + int nResult = 0; + struct TConfiguration *pConfiguration; + unsigned int nDevAVBstCtrl, nDevASlpCtrl; + unsigned int nDevBVBstCtrl, nDevBSlpCtrl; + unsigned int nConfig; + + if ((!pTAS2557->mpFirmware->mnConfigurations) + || (!pTAS2557->mpFirmware->mnPrograms)) { + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + if (bPowerOn) { + dev_info(pTAS2557->dev, "%s, will load VBoost state next time before power on\n", __func__); + pTAS2557->mbLoadVBoostPrePowerUp = true; + pTAS2557->mnVBoostNewState = vboost; + goto end; + } + + if (pTAS2557->mbLoadConfigurationPrePowerUp) + nConfig = pTAS2557->mnNewConfiguration; + else + nConfig = pTAS2557->mnCurrentConfiguration; + + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nConfig]); + + if (pTAS2557->mnVBoostState == TAS2557_VBST_NEED_DEFAULT) { + if (pConfiguration->mnDevices & channel_left) { + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_VBOOST_CTL_REG, &pTAS2557->mnVBoostDefaultCfg[0]); + if (nResult < 0) + goto end; + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_SLEEPMODE_CTL_REG, &pTAS2557->mnVBoostDefaultCfg[1]); + if (nResult < 0) + goto end; + } + if (pConfiguration->mnDevices & channel_right) { + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_VBOOST_CTL_REG, &pTAS2557->mnVBoostDefaultCfg[2]); + if (nResult < 0) + goto end; + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_SLEEPMODE_CTL_REG, &pTAS2557->mnVBoostDefaultCfg[3]); + if (nResult < 0) + goto end; + } + dev_dbg(pTAS2557->dev, "%s, get default VBoost\n", __func__); + pTAS2557->mnVBoostState = TAS2557_VBST_DEFAULT; + if ((vboost == TAS2557_VBST_DEFAULT) + || (vboost == TAS2557_VBST_NEED_DEFAULT)) { + dev_dbg(pTAS2557->dev, "%s, already default, bypass\n", __func__); + goto end; + } + } + + if (vboost) { + if (pConfiguration->mnDevices & channel_left) { + if (!(pTAS2557->mnVBoostState & TAS2557_VBST_A_ON)) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_left); + if (nResult < 0) + goto end; + nDevAVBstCtrl = 0x40; + nDevASlpCtrl = 0xb0; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_VBOOST_CTL_REG, nDevAVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_SLEEPMODE_CTL_REG, nDevASlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState |= TAS2557_VBST_A_ON; + dev_dbg(pTAS2557->dev, "%s, devA Boost On, %d\n", __func__, pTAS2557->mnVBoostState); + } + } else { + if (pTAS2557->mnVBoostState & TAS2557_VBST_A_ON) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_left); + if (nResult < 0) + goto end; + nDevAVBstCtrl = pTAS2557->mnVBoostDefaultCfg[0]; + nDevASlpCtrl = pTAS2557->mnVBoostDefaultCfg[1]; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_VBOOST_CTL_REG, nDevAVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_SLEEPMODE_CTL_REG, nDevASlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState &= ~TAS2557_VBST_A_ON; + dev_dbg(pTAS2557->dev, "%s, devA Boost Off, %d\n", __func__, pTAS2557->mnVBoostState); + } + } + + if (pConfiguration->mnDevices & channel_right) { + if (!(pTAS2557->mnVBoostState & TAS2557_VBST_B_ON)) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_right); + if (nResult < 0) + goto end; + nDevBVBstCtrl = 0x40; + nDevBSlpCtrl = 0xb0; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_VBOOST_CTL_REG, nDevBVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_SLEEPMODE_CTL_REG, nDevBSlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState |= TAS2557_VBST_B_ON; + dev_dbg(pTAS2557->dev, "%s, devB Boost On, %d\n", __func__, pTAS2557->mnVBoostState); + } + } else { + if (pTAS2557->mnVBoostState & TAS2557_VBST_B_ON) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_right); + if (nResult < 0) + goto end; + nDevBVBstCtrl = pTAS2557->mnVBoostDefaultCfg[2]; + nDevBSlpCtrl = pTAS2557->mnVBoostDefaultCfg[3]; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_VBOOST_CTL_REG, nDevBVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_SLEEPMODE_CTL_REG, nDevBSlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState &= ~TAS2557_VBST_B_ON; + dev_dbg(pTAS2557->dev, "%s, devB Boost Off, %d\n", __func__, pTAS2557->mnVBoostState); + } + } + } else { + nDevAVBstCtrl = pTAS2557->mnVBoostDefaultCfg[0]; + nDevASlpCtrl = pTAS2557->mnVBoostDefaultCfg[1]; + nDevBVBstCtrl = pTAS2557->mnVBoostDefaultCfg[2]; + nDevBSlpCtrl = pTAS2557->mnVBoostDefaultCfg[3]; + if (pTAS2557->mnVBoostState & TAS2557_VBST_A_ON) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_left); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_VBOOST_CTL_REG, nDevAVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_left, TAS2557_SLEEPMODE_CTL_REG, nDevASlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState &= ~TAS2557_VBST_A_ON; + dev_dbg(pTAS2557->dev, "%s, devA Boost default, %d\n", __func__, pTAS2557->mnVBoostState); + } + if (pTAS2557->mnVBoostState & TAS2557_VBST_B_ON) { + nResult = tas2557_update_VBstVolt(pTAS2557, channel_right); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_VBOOST_CTL_REG, nDevBVBstCtrl); + if (nResult < 0) + goto end; + nResult = pTAS2557->write(pTAS2557, channel_right, TAS2557_SLEEPMODE_CTL_REG, nDevBSlpCtrl); + if (nResult < 0) + goto end; + pTAS2557->mnVBoostState &= ~TAS2557_VBST_B_ON; + dev_dbg(pTAS2557->dev, "%s, devB Boost default, %d\n", __func__, pTAS2557->mnVBoostState); + } + } + +end: + + return 0; +} + +int tas2557_load_platdata(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioINT) + || gpio_is_valid(pTAS2557->mnRightChlGpioINT)) { + nResult = tas2557_configIRQ(pTAS2557); + if (nResult < 0) + goto end; + } + + nResult = tas2557_set_bit_rate(pTAS2557, channel_both, pTAS2557->mnI2SBits); + if (nResult < 0) + goto end; + + nResult = tas2557_SA_ctl_echoRef(pTAS2557); + if (nResult < 0) + goto end; + +end: + + return nResult; +} + +int tas2557_load_default(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; + + nResult = pTAS2557->write(pTAS2557, channel_both, TAS2557_TEST_MODE_REG, 0x0d); + if (nResult < 0) + goto end; + + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_BROADCAST_REG, &pTAS2557->mnLBroadcastSet); + if (nResult < 0) + goto end; + + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_BROADCAST_REG, &pTAS2557->mnRBroadcastSet); + if (nResult < 0) + goto end; + + dev_dbg(pTAS2557->dev, "%s, DevA Trim=0x%x, DevB Trim=0x%x\n", + __func__, pTAS2557->mnLBroadcastSet, pTAS2557->mnRBroadcastSet); + + nResult = tas2557_dev_load_data(pTAS2557, channel_both, p_tas2557_default_data); + if (nResult < 0) + goto end; + + nResult = tas2557_load_platdata(pTAS2557); + if (nResult < 0) + goto end; + + /* enable DOUT tri-state for extra BCLKs */ + nResult = pTAS2557->update_bits(pTAS2557, channel_both, TAS2557_ASI1_DAC_FORMAT_REG, 0x01, 0x01); + if (nResult < 0) + goto end; + + /* Interrupt pin, low-highZ, high active driven */ + nResult = pTAS2557->update_bits(pTAS2557, channel_both, TAS2557_GPIO_HIZ_CTRL2_REG, 0x30, 0x30); +end: + + return nResult; +} + +static void failsafe(struct tas2557_priv *pTAS2557) +{ + dev_err(pTAS2557->dev, "%s\n", __func__); + pTAS2557->mnErrCode |= ERROR_FAILSAFE; + + if (hrtimer_active(&pTAS2557->mtimer)) + hrtimer_cancel(&pTAS2557->mtimer); + if (pTAS2557->mnRestart < RESTART_MAX) { + pTAS2557->mnRestart ++; + msleep(100); + dev_err(pTAS2557->dev, "I2C COMM error, restart SmartAmp.\n"); + schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(100)); + return; + } + pTAS2557->enableIRQ(pTAS2557, channel_both, false); + tas2557_dev_load_data(pTAS2557, channel_both, p_tas2557_shutdown_data); + pTAS2557->mbPowerUp = false; + pTAS2557->hw_reset(pTAS2557); + pTAS2557->write(pTAS2557, channel_both, TAS2557_SW_RESET_REG, 0x01); + udelay(1000); + pTAS2557->write(pTAS2557, channel_both, TAS2557_SPK_CTRL_REG, 0x04); + if (pTAS2557->mpFirmware != NULL) + tas2557_clear_firmware(pTAS2557->mpFirmware); +} + +int tas2557_checkPLL(struct tas2557_priv *pTAS2557) +{ + int nResult = 0; +/* + * TO DO + */ + return nResult; +} + +/* + * tas2557_load_coefficient + */ +static int tas2557_load_coefficient(struct tas2557_priv *pTAS2557, + int nPrevConfig, int nNewConfig, bool bPowerOn) +{ + int nResult = 0; + struct TPLL *pPLL; + struct TProgram *pProgram; + struct TConfiguration *pPrevConfiguration; + struct TConfiguration *pNewConfiguration; + enum channel chl; + bool bRestorePower = false; + + if (!pTAS2557->mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + if (nNewConfig >= pTAS2557->mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "%s, invalid configuration New=%d, total=%d\n", + __func__, nNewConfig, pTAS2557->mpFirmware->mnConfigurations); + goto end; + } + + if (nPrevConfig < 0) { + pPrevConfiguration = NULL; + chl = channel_both; + } else if (nPrevConfig == nNewConfig) { + dev_dbg(pTAS2557->dev, "%s, config [%d] already loaded\n", + __func__, nNewConfig); + goto end; + } else { + pPrevConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nPrevConfig]); + chl = pPrevConfiguration->mnDevices; + } + + pNewConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nNewConfig]); + pTAS2557->mnCurrentConfiguration = nNewConfig; + if (pPrevConfiguration) { + if ((pPrevConfiguration->mnPLL == pNewConfiguration->mnPLL) + && (pPrevConfiguration->mnDevices == pNewConfiguration->mnDevices)) { + dev_dbg(pTAS2557->dev, "%s, PLL and device same\n", __func__); + goto prog_coefficient; + } + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + if (bPowerOn) { + dev_dbg(pTAS2557->dev, "%s, power down to load new snapshot\n", __func__); + if (hrtimer_active(&pTAS2557->mtimer)) + hrtimer_cancel(&pTAS2557->mtimer); + + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) + pTAS2557->enableIRQ(pTAS2557, channel_both, false); + dev_dbg(pTAS2557->dev, "%s, shutdown %d\n", __func__, chl); + nResult = tas2557_dev_load_data(pTAS2557, chl, p_tas2557_shutdown_data); + if (nResult < 0) + goto end; + bRestorePower = true; + } + + /* load PLL */ + pPLL = &(pTAS2557->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]); + dev_dbg(pTAS2557->dev, "load PLL: %s block for Configuration %s\n", + pPLL->mpName, pNewConfiguration->mpName); + nResult = tas2557_load_block(pTAS2557, &(pPLL->mBlock)); + if (nResult < 0) + goto end; + pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate; + + dev_dbg(pTAS2557->dev, "load configuration %s conefficient pre block\n", + pNewConfiguration->mpName); + if (pNewConfiguration->mnDevices & channel_left) { + nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData), TAS2557_BLOCK_CFG_PRE_DEV_A); + if (nResult < 0) + goto end; + } + if (pNewConfiguration->mnDevices & channel_right) { + nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData), TAS2557_BLOCK_CFG_PRE_DEV_B); + if (nResult < 0) + goto end; + } + +prog_coefficient: + dev_dbg(pTAS2557->dev, "load new configuration: %s, coeff block data\n", + pNewConfiguration->mpName); + if (pNewConfiguration->mnDevices & channel_left) { + nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData), + TAS2557_BLOCK_CFG_COEFF_DEV_A); + if (nResult < 0) + goto end; + if (pTAS2557->mnChannelState == TAS2557_DM_AD_BD) { + nResult = pTAS2557->bulk_read(pTAS2557, + channel_left, TAS2557_SA_PG2P1_CHL_CTRL_REG, pTAS2557->mnDevAChlData, 16); + if (nResult < 0) + goto end; + } + } + if (pNewConfiguration->mnDevices & channel_right) { + nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData), + TAS2557_BLOCK_CFG_COEFF_DEV_B); + if (nResult < 0) + goto end; + if (pTAS2557->mnChannelState == TAS2557_DM_AD_BD) { + nResult = pTAS2557->bulk_read(pTAS2557, + channel_right, TAS2557_SA_PG2P1_CHL_CTRL_REG, pTAS2557->mnDevBChlData, 16); + if (nResult < 0) + goto end; + } + } + + if (pTAS2557->mnChannelState != TAS2557_DM_AD_BD) { + nResult = tas2557_SA_DevChnSetup(pTAS2557, pTAS2557->mnChannelState); + if (nResult < 0) + goto end; + } + + if (pTAS2557->mpCalFirmware->mnCalibrations) { + nResult = tas2557_set_calibration(pTAS2557, pTAS2557->mnCurrentCalibration); + if (nResult < 0) + goto end; + } + + if (bRestorePower) { + dev_dbg(pTAS2557->dev, "%s, set vboost, before power on %d\n", + __func__, pTAS2557->mnVBoostState); + nResult = tas2557_set_VBoost(pTAS2557, pTAS2557->mnVBoostState, false); + if (nResult < 0) + goto end; + + pTAS2557->clearIRQ(pTAS2557); + dev_dbg(pTAS2557->dev, "%s, %s, load startup %d\n", + __func__, pNewConfiguration->mpName, pNewConfiguration->mnDevices); + nResult = tas2557_dev_load_data(pTAS2557, pNewConfiguration->mnDevices, p_tas2557_startup_data); + if (nResult < 0) + goto end; + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + nResult = tas2557_checkPLL(pTAS2557); + if (nResult < 0) { + nResult = tas2557_dev_load_data(pTAS2557, pNewConfiguration->mnDevices, p_tas2557_shutdown_data); + pTAS2557->mbPowerUp = false; + goto end; + } + } + dev_dbg(pTAS2557->dev, + "device powered up, load unmute\n"); + nResult = tas2557_dev_load_data(pTAS2557, pNewConfiguration->mnDevices, p_tas2557_unmute_data); + if (nResult < 0) + goto end; + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + pTAS2557->enableIRQ(pTAS2557, pNewConfiguration->mnDevices, true); + if (!hrtimer_active(&pTAS2557->mtimer)) { + pTAS2557->mnDieTvReadCounter = 0; + hrtimer_start(&pTAS2557->mtimer, + ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + } + } +end: + if (nResult < 0) + dev_err(pTAS2557->dev, "%s, load new conf %s error\n", __func__, pNewConfiguration->mpName); + + return nResult; +} + +extern atomic_t tas2557_ref_count; +int tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable) +{ + int nResult = 0; + struct TProgram *pProgram; + struct TConfiguration *pConfiguration; + unsigned int nValue; + + dev_dbg(pTAS2557->dev, "%s: ref_count = %d\n", __func__, atomic_read(&tas2557_ref_count)); + if (bEnable) { + if (atomic_read(&tas2557_ref_count) > 0) { + atomic_inc(&tas2557_ref_count); + dev_dbg(pTAS2557->dev, "%s: Already enabled\n", __func__); + return 0; + } + atomic_inc(&tas2557_ref_count); + } else if (!bEnable) { + if (atomic_read(&tas2557_ref_count) > 1) { + atomic_dec(&tas2557_ref_count); + dev_dbg(pTAS2557->dev, "%s: Is called by others, not to be disabled\n", __func__); + return 0; + } + atomic_dec(&tas2557_ref_count); + } + dev_dbg(pTAS2557->dev, "%s: ref_count now = %d\n", __func__, atomic_read(&tas2557_ref_count)); + + dev_dbg(pTAS2557->dev, "Enable: %d\n", bEnable); + + if ((pTAS2557->mpFirmware->mnPrograms == 0) + || (pTAS2557->mpFirmware->mnConfigurations == 0)) { + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + /* check safe guard*/ + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_SAFE_GUARD_REG, &nValue); + if (nResult < 0) + goto end; + if ((nValue&0xff) != TAS2557_SAFE_GUARD_PATTERN) { + dev_err(pTAS2557->dev, "ERROR Left channel safe guard failure!\n"); + nResult = -EPIPE; + pTAS2557->mnErrCode = ERROR_SAFE_GUARD; + pTAS2557->mbPowerUp = true; + goto end; + } + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_SAFE_GUARD_REG, &nValue); + if (nResult < 0) + goto end; + if ((nValue&0xff) != TAS2557_SAFE_GUARD_PATTERN) { + dev_err(pTAS2557->dev, "ERROR right channel safe guard failure!\n"); + nResult = -EPIPE; + pTAS2557->mnErrCode = ERROR_SAFE_GUARD; + pTAS2557->mbPowerUp = true; + goto end; + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + if (bEnable) { + if (!pTAS2557->mbPowerUp) { + if (pTAS2557->mbLoadConfigurationPrePowerUp) { + dev_dbg(pTAS2557->dev, "load coefficient before power\n"); + pTAS2557->mbLoadConfigurationPrePowerUp = false; + nResult = tas2557_load_coefficient(pTAS2557, + pTAS2557->mnCurrentConfiguration, pTAS2557->mnNewConfiguration, false); + if (nResult < 0) + goto end; + } + + if (pTAS2557->mbLoadVBoostPrePowerUp) { + dev_dbg(pTAS2557->dev, "%s, cfg boost before power on new %d, current=%d\n", + __func__, pTAS2557->mnVBoostNewState, pTAS2557->mnVBoostState); + nResult = tas2557_set_VBoost(pTAS2557, pTAS2557->mnVBoostNewState, false); + if (nResult < 0) + goto end; + pTAS2557->mbLoadVBoostPrePowerUp = false; + } + + pTAS2557->clearIRQ(pTAS2557); + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + /* power on device */ + dev_dbg(pTAS2557->dev, "%s: %s, %d, load startup sequence\n", + __func__, pConfiguration->mpName, pConfiguration->mnDevices); + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_startup_data); + if (nResult < 0) + goto end; + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + nResult = tas2557_checkPLL(pTAS2557); + if (nResult < 0) { + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_shutdown_data); + goto end; + } + } + dev_dbg(pTAS2557->dev, "Enable: load unmute sequence\n"); + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_unmute_data); + if (nResult < 0) + goto end; + + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + /* turn on IRQ */ + pTAS2557->enableIRQ(pTAS2557, pConfiguration->mnDevices, true); + if (!hrtimer_active(&pTAS2557->mtimer)) { + pTAS2557->mnDieTvReadCounter = 0; + hrtimer_start(&pTAS2557->mtimer, + ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + } + pTAS2557->mbPowerUp = true; + } + } else { + if (pTAS2557->mbPowerUp) { + if (hrtimer_active(&pTAS2557->mtimer)) + hrtimer_cancel(&pTAS2557->mtimer); + + dev_dbg(pTAS2557->dev, "Enable: load shutdown sequence\n"); + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + /* turn off IRQ */ + pTAS2557->enableIRQ(pTAS2557, channel_both, false); + } + dev_dbg(pTAS2557->dev, "%s, %s, shutdown %d\n", __func__, pConfiguration->mpName, pConfiguration->mnDevices); + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_shutdown_data); + if (nResult < 0) + goto end; + + pTAS2557->mbPowerUp = false; + } + } + + nResult = 0; + +end: + if (nResult < 0) { + if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK| ERROR_SAFE_GUARD)) + failsafe(pTAS2557); + } + + return nResult; +} + +int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSamplingRate) +{ + int nResult = 0; + struct TConfiguration *pConfiguration; + unsigned int nConfiguration; + + dev_dbg(pTAS2557->dev, "tas2557_setup_clocks: nSamplingRate = %d [Hz]\n", + nSamplingRate); + + if ((!pTAS2557->mpFirmware->mpPrograms) || + (!pTAS2557->mpFirmware->mpConfigurations)) { + dev_err(pTAS2557->dev, "%s: Firmware not loaded\n", __func__); + nResult = -EINVAL; + goto end; + } + + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + if (pConfiguration->mnSamplingRate == nSamplingRate) { + dev_info(pTAS2557->dev, "Sampling rate for current configuration matches: %d\n", + nSamplingRate); + nResult = 0; + goto end; + } + + for (nConfiguration = 0; + nConfiguration < pTAS2557->mpFirmware->mnConfigurations; + nConfiguration++) { + pConfiguration = + &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]); + if ((pConfiguration->mnSamplingRate == nSamplingRate) + && (pConfiguration->mnProgram == pTAS2557->mnCurrentProgram)) { + dev_info(pTAS2557->dev, + "Found configuration: %s, with compatible sampling rate %d\n", + pConfiguration->mpName, nSamplingRate); + nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false); + goto end; + } + } + + dev_err(pTAS2557->dev, "Cannot find a configuration that supports sampling rate: %d\n", + nSamplingRate); + +end: + + return nResult; +} + +static void fw_print_header(struct tas2557_priv *pTAS2557, struct TFirmware *pFirmware) +{ + dev_info(pTAS2557->dev, "FW Size = %d", pFirmware->mnFWSize); + dev_info(pTAS2557->dev, "Checksum = 0x%04X", pFirmware->mnChecksum); + dev_info(pTAS2557->dev, "PPC Version = 0x%04X", pFirmware->mnPPCVersion); + dev_info(pTAS2557->dev, "FW Version = 0x%04X", pFirmware->mnFWVersion); + dev_info(pTAS2557->dev, "Driver Version= 0x%04X", pFirmware->mnDriverVersion); + dev_info(pTAS2557->dev, "Timestamp = %d", pFirmware->mnTimeStamp); + dev_info(pTAS2557->dev, "DDC Name = %s", pFirmware->mpDDCName); + dev_info(pTAS2557->dev, "Description = %s", pFirmware->mpDescription); +} + +inline unsigned int fw_convert_number(unsigned char *pData) +{ + return pData[3] + (pData[2] << 8) + (pData[1] << 16) + (pData[0] << 24); +} + +static int fw_parse_header(struct tas2557_priv *pTAS2557, + 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(pTAS2557->dev, "Firmware: Header too short"); + return -EINVAL; + } + + if (memcmp(pData, pMagicNumber, 4)) { + dev_err(pTAS2557->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); + pData += 4; + + pFirmware->mnTimeStamp = fw_convert_number(pData); + pData += 4; + + memcpy(pFirmware->mpDDCName, pData, 64); + pData += 64; + + n = strlen(pData); + pFirmware->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL); + pData += n + 1; + + if ((pData - pDataStart) >= nSize) { + dev_err(pTAS2557->dev, "Firmware: Header too short after DDC description"); + return -EINVAL; + } + + pFirmware->mnDeviceFamily = fw_convert_number(pData); + pData += 4; + + if (pFirmware->mnDeviceFamily != 0) { + dev_err(pTAS2557->dev, + "deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily); + return -EINVAL; + } + + pFirmware->mnDevice = fw_convert_number(pData); + pData += 4; + + if (pFirmware->mnDevice != 3) { + dev_err(pTAS2557->dev, + "device %d, not TAS2557 Dual Mono", pFirmware->mnDevice); + return -EINVAL; + } + + fw_print_header(pTAS2557, pFirmware); + return pData - pDataStart; +} + +static int fw_parse_block_data(struct tas2557_priv *pTAS2557, 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 tas2557_priv *pTAS2557, 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(pTAS2557, pFirmware, + &(pImageData->mpBlocks[nBlock]), pData); + pData += n; + } + return pData - pDataStart; +} + +static int fw_parse_pll_data(struct tas2557_priv *pTAS2557, + 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(pTAS2557, pFirmware, &(pPLL->mBlock), pData); + pData += n; + } + +end: + return pData - pDataStart; +} + +static int fw_parse_program_data(struct tas2557_priv *pTAS2557, + 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(pTAS2557, pFirmware, &(pProgram->mData), pData); + pData += n; + } + +end: + + return pData - pDataStart; +} + +static int fw_parse_configuration_data(struct tas2557_priv *pTAS2557, + 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 = channel_both; + + 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; + } + + n = fw_parse_data(pTAS2557, pFirmware, &(pConfiguration->mData), pData); + pData += n; + } + +end: + + return pData - pDataStart; +} + +int fw_parse_calibration_data(struct tas2557_priv *pTAS2557, + 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(pTAS2557, pFirmware, &(pCalibration->mData), pData); + pData += n; + } + +end: + + return pData - pDataStart; +} + +static int fw_parse(struct tas2557_priv *pTAS2557, + struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize) +{ + int nPosition = 0; + + nPosition = fw_parse_header(pTAS2557, pFirmware, pData, nSize); + if (nPosition < 0) { + dev_err(pTAS2557->dev, "Firmware: Wrong Header"); + return -EINVAL; + } + + if (nPosition >= nSize) { + dev_err(pTAS2557->dev, "Firmware: Too short"); + return -EINVAL; + } + + pData += nPosition; + nSize -= nPosition; + nPosition = 0; + + nPosition = fw_parse_pll_data(pTAS2557, pFirmware, pData); + + pData += nPosition; + nSize -= nPosition; + nPosition = 0; + + nPosition = fw_parse_program_data(pTAS2557, pFirmware, pData); + + pData += nPosition; + nSize -= nPosition; + nPosition = 0; + + nPosition = fw_parse_configuration_data(pTAS2557, pFirmware, pData); + + pData += nPosition; + nSize -= nPosition; + nPosition = 0; + + if (nSize > 64) + nPosition = fw_parse_calibration_data(pTAS2557, 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 isInPageYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len) +{ + int nResult = 0; + + if (nBook == TAS2557_YRAM_BOOK1) { + if (nPage == TAS2557_YRAM1_PAGE) { + if (nReg >= TAS2557_YRAM1_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else if ((nReg + len) > TAS2557_YRAM1_START_REG) { + pCRCData->mnOffset = TAS2557_YRAM1_START_REG; + pCRCData->mnLen = len - (TAS2557_YRAM1_START_REG - nReg); + nResult = 1; + } else + nResult = 0; + } else if (nPage == TAS2557_YRAM3_PAGE) { + if (nReg > TAS2557_YRAM3_END_REG) { + nResult = 0; + } else if (nReg >= TAS2557_YRAM3_START_REG) { + if ((nReg + len) > TAS2557_YRAM3_END_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = TAS2557_YRAM3_END_REG - nReg + 1; + nResult = 1; + } else { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } + } else { + if ((nReg + (len - 1)) < TAS2557_YRAM3_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = TAS2557_YRAM3_START_REG; + pCRCData->mnLen = len - (TAS2557_YRAM3_START_REG - nReg); + nResult = 1; + } + } + } + } else if (nBook == TAS2557_YRAM_BOOK2) { + if (nPage == TAS2557_YRAM5_PAGE) { + if (nReg > TAS2557_YRAM5_END_REG) { + nResult = 0; + } else if (nReg >= TAS2557_YRAM5_START_REG) { + if ((nReg + len) > TAS2557_YRAM5_END_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = TAS2557_YRAM5_END_REG - nReg + 1; + nResult = 1; + } else { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } + } else { + if ((nReg + (len - 1)) < TAS2557_YRAM5_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = TAS2557_YRAM5_START_REG; + pCRCData->mnLen = len - (TAS2557_YRAM5_START_REG - nReg); + nResult = 1; + } + } + } + } else + nResult = 0; + + return nResult; +} + +static int isInBlockYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len) +{ + int nResult; + + if (nBook == TAS2557_YRAM_BOOK1) { + if (nPage < TAS2557_YRAM2_START_PAGE) + nResult = 0; + else if (nPage <= TAS2557_YRAM2_END_PAGE) { + if (nReg > TAS2557_YRAM2_END_REG) + nResult = 0; + else if (nReg >= TAS2557_YRAM2_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else { + if ((nReg + (len - 1)) < TAS2557_YRAM2_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = TAS2557_YRAM2_START_REG; + pCRCData->mnLen = nReg + len - TAS2557_YRAM2_START_REG; + nResult = 1; + } + } + } else + nResult = 0; + } else if (nBook == TAS2557_YRAM_BOOK2) { + if (nPage < TAS2557_YRAM4_START_PAGE) + nResult = 0; + else if (nPage <= TAS2557_YRAM4_END_PAGE) { + if (nReg > TAS2557_YRAM2_END_REG) + nResult = 0; + else if (nReg >= TAS2557_YRAM2_START_REG) { + pCRCData->mnOffset = nReg; + pCRCData->mnLen = len; + nResult = 1; + } else { + if ((nReg + (len - 1)) < TAS2557_YRAM2_START_REG) + nResult = 0; + else { + pCRCData->mnOffset = TAS2557_YRAM2_START_REG; + pCRCData->mnLen = nReg + len - TAS2557_YRAM2_START_REG; + nResult = 1; + } + } + } else + nResult = 0; + } else + nResult = 0; + + return nResult; +} + +static int isYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData, + unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len) +{ + int nResult; + + nResult = isInPageYRAM(pTAS2557, pCRCData, nBook, nPage, nReg, len); + + if (nResult == 0) + nResult = isInBlockYRAM(pTAS2557, 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 tas2557_priv *pTAS2557, 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 ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG)) + && (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG)) + && (nReg >= TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG)) + && (nReg <= (TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG) + 4))) { + /* DSP swap command, pass */ + nResult = 0; + goto end; + } + + nResult = isYRAM(pTAS2557, &sCRCData, nBook, nPage, nReg, 1); + if (nResult == 1) { + if (chl == channel_broadcast) { + nResult = tas2557_exit_broadcast_mode(pTAS2557); + if (nResult < 0) + goto end; + } + + if ((chl & channel_left) || (chl == channel_broadcast)) { + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_REG(nBook, nPage, nReg), &nData1); + if (nResult < 0) + goto end; + } + if ((chl & channel_right) || (chl == channel_broadcast)) { + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_REG(nBook, nPage, nReg), &nData2); + if (nResult < 0) + goto end; + } + + if (chl == channel_broadcast) + nResult = tas2557_enter_broadcast_mode(pTAS2557); + + if ((chl == channel_both) || (chl == channel_broadcast)) { + if ((nData1 != nData2) || (nData1 != nValue)) { + dev_err(pTAS2557->dev, + "error (line %d),B[0x%x]P[0x%x]R[0x%x] W[0x%x], R1[0x%x], R2[0x%x]\n", + __LINE__, nBook, nPage, nReg, nValue, nData1, nData2); + nResult = -EAGAIN; + pTAS2557->mnErrCode |= ERROR_YRAM_CRCCHK; + goto end; + } + nRegVal = nData1; + } else if (chl == channel_left) { + if (nData1 != nValue) { + dev_err(pTAS2557->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; + pTAS2557->mnErrCode |= ERROR_YRAM_CRCCHK; + goto end; + } + nRegVal = nData1; + } else if (chl == channel_right) { + if (nData2 != nValue) { + dev_err(pTAS2557->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; + pTAS2557->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 tas2557_priv *pTAS2557, 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(pTAS2557->dev, "firmware error\n"); + goto end; + } + + if ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG)) + && (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG)) + && (nReg == TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /* DSP swap command, pass */ + nResult = 0; + goto end; + } + + nResult = isYRAM(pTAS2557, &TCRCData, nBook, nPage, nReg, len); + if (nResult == 1) { + if (len == 1) { + dev_err(pTAS2557->dev, "firmware error\n"); + nResult = -EINVAL; + goto end; + } else { + if (chl == channel_broadcast) { + nResult = tas2557_exit_broadcast_mode(pTAS2557); + if (nResult < 0) + goto end; + } + + if ((chl & channel_left) || (chl == channel_broadcast)) { + nResult = pTAS2557->bulk_read(pTAS2557, channel_left, + TAS2557_REG(nBook, nPage, TCRCData.mnOffset), nBuf1, TCRCData.mnLen); + if (nResult < 0) + goto end; + } + if ((chl & channel_right) || (chl == channel_broadcast)) { + nResult = pTAS2557->bulk_read(pTAS2557, channel_right, + TAS2557_REG(nBook, nPage, TCRCData.mnOffset), nBuf2, TCRCData.mnLen); + if (nResult < 0) + goto end; + } + + if (chl == channel_broadcast) + nResult = tas2557_enter_broadcast_mode(pTAS2557); + + if ((chl == channel_both) || (chl == channel_broadcast)) { + if (memcmp(nBuf1, nBuf2, TCRCData.mnLen) != 0) { + dev_err(pTAS2557->dev, + "error (line %d), B[0x%x]P[0x%x]R[0x%x] doesn't match\n", + __LINE__, nBook, nPage, nReg); + nResult = -EAGAIN; + pTAS2557->mnErrCode |= ERROR_YRAM_CRCCHK; + goto end; + } + pRegVal = nBuf1; + } else if (chl == channel_left) + pRegVal = nBuf1; + else if (chl == channel_right) + pRegVal = nBuf2; + else { + dev_err(pTAS2557->dev, "channel error %d\n", chl); + nResult = -EINVAL; + goto end; + } + + for (i = 0; i < TCRCData.mnLen; i++) { + if ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG)) + && (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG)) + && ((i + TCRCData.mnOffset) + >= TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG)) + && ((i + TCRCData.mnOffset) + <= (TAS2557_PAGE_REG(TAS2557_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 tas2557_load_block(struct tas2557_priv *pTAS2557, 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, nValue2; + unsigned int nLength; + unsigned int nSleep; + enum channel chl; + struct TConfiguration *pConfiguration; + unsigned char nCRCChkSum = 0; + int nRetry = 6; + unsigned char *pData = pBlock->mpData; + + dev_dbg(pTAS2557->dev, "%s: Type = %d, commands = %d\n", __func__, + pBlock->mnType, pBlock->mnCommands); + if (pBlock->mnType == TAS2557_BLOCK_PLL) { + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + chl = pConfiguration->mnDevices; + dev_dbg(pTAS2557->dev, "%s: PLL chl = %d\n", __func__, chl); + } else if ((pBlock->mnType == TAS2557_BLOCK_PGM_DEV_A) + || (pBlock->mnType == TAS2557_BLOCK_CFG_COEFF_DEV_A) + || (pBlock->mnType == TAS2557_BLOCK_CFG_PRE_DEV_A)) { + chl = channel_left; + } else if ((pBlock->mnType == TAS2557_BLOCK_PGM_DEV_B) + || (pBlock->mnType == TAS2557_BLOCK_CFG_COEFF_DEV_B) + || (pBlock->mnType == TAS2557_BLOCK_CFG_PRE_DEV_B)) { + chl = channel_right; + } else if (pBlock->mnType == TAS2557_BLOCK_PGM_ALL) { + chl = channel_broadcast; + } else { + dev_err(pTAS2557->dev, "block type error %d\n", pBlock->mnType); + nResult = -EINVAL; + goto end; + } + +start: + if (pBlock->mbPChkSumPresent) { + if (chl == channel_broadcast) + nResult = pTAS2557->write(pTAS2557, channel_both, TAS2557_CRC_RESET_REG, 1); + else + nResult = pTAS2557->write(pTAS2557, chl, TAS2557_CRC_RESET_REG, 1); + if (nResult < 0) { + dev_err(pTAS2557->dev, "I2C err\n"); + goto end; + } + } + + if (pBlock->mbYChkSumPresent) + nCRCChkSum = 0; + + nCommand = 0; + + if (chl == channel_broadcast) { + nResult = tas2557_enter_broadcast_mode(pTAS2557); + if (nResult < 0) + goto end; + } + + 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 = pTAS2557->write(pTAS2557, chl, TAS2557_REG(nBook, nPage, nOffset), nData); + + if (pBlock->mbYChkSumPresent) { + nResult = doSingleRegCheckSum(pTAS2557, 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) { + pTAS2557->bulk_write(pTAS2557, chl, TAS2557_REG(nBook, nPage, nOffset), pData + 3, nLength); + + if (pBlock->mbYChkSumPresent) { + nResult = doMultiRegCheckSum(pTAS2557, chl, nBook, nPage, nOffset, nLength); + if (nResult < 0) + goto check; + nCRCChkSum += (unsigned char)nResult; + } + } else { + pTAS2557->write(pTAS2557, chl, TAS2557_REG(nBook, nPage, nOffset), pData[3]); + + if (pBlock->mbYChkSumPresent) { + nResult = doSingleRegCheckSum(pTAS2557, 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 (chl == channel_broadcast) + nResult = tas2557_exit_broadcast_mode(pTAS2557); + + if (pBlock->mbPChkSumPresent) { + if ((chl & channel_left) || (chl == channel_broadcast)) + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_CRC_CHECKSUM_REG, &nValue1); + if ((chl & channel_right) || (chl == channel_broadcast)) + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_CRC_CHECKSUM_REG, &nValue2); + + if ((chl == channel_both) || (chl == channel_broadcast)) { + if ((nValue1 != nValue2) || (nValue1 != pBlock->mnPChkSum)) { + dev_err(pTAS2557->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x, 0x%x\n", + pBlock->mnPChkSum, (nValue1&0xff), (nValue2&0xff)); + nResult = -EAGAIN; + pTAS2557->mnErrCode |= ERROR_PRAM_CRCCHK; + goto check; + } + } else if (chl == channel_left) { + if (nValue1 != pBlock->mnPChkSum) { + dev_err(pTAS2557->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x\n", + pBlock->mnPChkSum, (nValue1&0xff)); + nResult = -EAGAIN; + pTAS2557->mnErrCode |= ERROR_PRAM_CRCCHK; + goto check; + } + } else if (chl == channel_right) { + if (nValue2 != pBlock->mnPChkSum) { + dev_err(pTAS2557->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x\n", + pBlock->mnPChkSum, (nValue2&0xff)); + pTAS2557->mnErrCode |= ERROR_PRAM_CRCCHK; + nResult = -EAGAIN; + goto check; + } + } + nResult = 0; + pTAS2557->mnErrCode &= ~ERROR_PRAM_CRCCHK; + dev_dbg(pTAS2557->dev, "Block[0x%x] PChkSum match\n", pBlock->mnType); + } + + if (pBlock->mbYChkSumPresent) { + if (nCRCChkSum != pBlock->mnYChkSum) { + dev_err(pTAS2557->dev, "Block YChkSum Error: FW = 0x%x, YCRC = 0x%x\n", + pBlock->mnYChkSum, nCRCChkSum); + nResult = -EAGAIN; + pTAS2557->mnErrCode |= ERROR_YRAM_CRCCHK; + goto check; + } + pTAS2557->mnErrCode &= ~ERROR_YRAM_CRCCHK; + nResult = 0; + dev_dbg(pTAS2557->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(pTAS2557->dev, "Block (%d) load error\n", + pBlock->mnType); + } + return nResult; +} + +static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData, unsigned int nType) +{ + int nResult = 0; + unsigned int nBlock; + struct TBlock *pBlock; + + dev_dbg(pTAS2557->dev, + "TAS2557 load data: %s, Blocks = %d, Block Type = %d\n", pData->mpName, pData->mnBlocks, nType); + + for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) { + pBlock = &(pData->mpBlocks[nBlock]); + if (pBlock->mnType == nType) { + nResult = tas2557_load_block(pTAS2557, pBlock); + if (nResult < 0) + break; + } + } + + return nResult; +} + +static int tas2557_load_configuration(struct tas2557_priv *pTAS2557, + unsigned int nConfiguration, bool bLoadSame) +{ + int nResult = 0; + struct TConfiguration *pCurrentConfiguration = NULL; + struct TConfiguration *pNewConfiguration = NULL; + + dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, nConfiguration); + + if ((!pTAS2557->mpFirmware->mpPrograms) || + (!pTAS2557->mpFirmware->mpConfigurations)) { + dev_err(pTAS2557->dev, "%s: Firmware not loaded\n", __func__); + nResult = 0; + goto end; + } + + if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n", + nConfiguration); + nResult = 0; + goto end; + } + + if ((!pTAS2557->mbLoadConfigurationPrePowerUp) + && (nConfiguration == pTAS2557->mnCurrentConfiguration) + && (!bLoadSame)) { + dev_info(pTAS2557->dev, "Configuration %d is already loaded\n", + nConfiguration); + nResult = 0; + goto end; + } + + pCurrentConfiguration = + &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + pNewConfiguration = + &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]); + if (pNewConfiguration->mnProgram != pCurrentConfiguration->mnProgram) { + dev_err(pTAS2557->dev, "Configuration %d, %s doesn't share the same program as current %d\n", + nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram); + nResult = 0; + goto end; + } + + if (pNewConfiguration->mnPLL >= pTAS2557->mpFirmware->mnPLLs) { + dev_err(pTAS2557->dev, "Configuration %d, %s doesn't have a valid PLL index %d\n", + nConfiguration, pNewConfiguration->mpName, pNewConfiguration->mnPLL); + nResult = 0; + goto end; + } + + if (pTAS2557->mbPowerUp) { + dev_err(pTAS2557->dev, "%s, device power on, load new conf[%d] %s\n", __func__, + nConfiguration, pNewConfiguration->mpName); + pTAS2557->mbLoadConfigurationPrePowerUp = false; + nResult = tas2557_load_coefficient(pTAS2557, pTAS2557->mnCurrentConfiguration, nConfiguration, true); + } else { + dev_dbg(pTAS2557->dev, + "TAS2557 was powered down, will load coefficient when power up\n"); + pTAS2557->mbLoadConfigurationPrePowerUp = true; + pTAS2557->mnNewConfiguration = nConfiguration; + } + +end: + + if (nResult < 0) { + if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK)) + failsafe(pTAS2557); + } + + return nResult; +} + +int tas2557_set_config(struct tas2557_priv *pTAS2557, int config) +{ + struct TConfiguration *pConfiguration; + struct TProgram *pProgram; + unsigned int nProgram = pTAS2557->mnCurrentProgram; + unsigned int nConfiguration = config; + int nResult = 0; + + if ((!pTAS2557->mpFirmware->mpPrograms) || + (!pTAS2557->mpFirmware->mpConfigurations)) { + dev_err(pTAS2557->dev, "%s: Firmware not loaded\n", __func__); + nResult = -EINVAL; + goto end; + } + + if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n", + nConfiguration); + nResult = -EINVAL; + goto end; + } + + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]); + pProgram = &(pTAS2557->mpFirmware->mpPrograms[nProgram]); + + if (nProgram != pConfiguration->mnProgram) { + dev_err(pTAS2557->dev, + "Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n", + nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram, + nProgram, pProgram->mpName); + nResult = -EINVAL; + goto end; + } + + dev_dbg(pTAS2557->dev, "%s, load new conf %s\n", __func__, pConfiguration->mpName); + nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false); + +end: + + return nResult; +} + +void tas2557_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 tas2557_load_calibration(struct tas2557_priv *pTAS2557, char *pFileName) +{ + int nResult = 0; + + struct file *nFile=NULL; + unsigned char pBuffer[1000]; + int nSize = 0; + loff_t pos = 0; + + dev_dbg(pTAS2557->dev, "%s:\n", __func__); + + nFile = filp_open(pFileName, O_RDONLY, 0); + + dev_info(pTAS2557->dev, "TAS2557 calibration file = %s\n", pFileName); + if (IS_ERR(nFile)) { + if (PTR_ERR(nFile) == -ENOENT) + dev_err(pTAS2557->dev, "TAS2557 calibration file %s is not exit\n", pFileName); + else + dev_err(pTAS2557->dev, "TAS2557 cannot open calibration file: %s errno:%d\n", pFileName, (int)PTR_ERR(nFile)); + } else { + pos = nFile->f_pos; + nSize = kernel_read(nFile, pBuffer, 1000, &pos); + filp_close(nFile,NULL); + } + + if (!nSize) + goto end; + + tas2557_clear_firmware(pTAS2557->mpCalFirmware); + dev_info(pTAS2557->dev, "TAS2557 calibration file size = %d\n", nSize); + nResult = fw_parse(pTAS2557, pTAS2557->mpCalFirmware, pBuffer, nSize); + + if (nResult) + dev_err(pTAS2557->dev, "TAS2557 calibration file is corrupt\n"); + else + dev_info(pTAS2557->dev, "TAS2557 calibration: %d calibrations\n", pTAS2557->mpCalFirmware->mnCalibrations); +end: + return nResult; +} + +static bool tas2557_get_coefficient_in_block(struct tas2557_priv *pTAS2557, + struct TBlock *pBlock, int nReg, int *pnValue) +{ + int nCoefficient = 0; + bool bFound = false; + unsigned char *pCommands; + int nBook, nPage, nOffset, len; + int i, n; + + pCommands = pBlock->mpData; + for (i = 0 ; i < pBlock->mnCommands;) { + nBook = pCommands[4 * i + 0]; + nPage = pCommands[4 * i + 1]; + nOffset = pCommands[4 * i + 2]; + if ((nOffset < 0x7f) || (nOffset == 0x81)) + i++; + else if (nOffset == 0x85) { + len = ((int)nBook << 8) | nPage; + nBook = pCommands[4 * i + 4]; + nPage = pCommands[4 * i + 5]; + nOffset = pCommands[4 * i + 6]; + n = 4 * i + 7; + i += 2; + i += ((len - 1) / 4); + if ((len - 1) % 4) + i++; + if ((nBook != TAS2557_BOOK_ID(nReg)) + || (nPage != TAS2557_PAGE_ID(nReg))) + continue; + if (nOffset > TAS2557_PAGE_REG(nReg)) + continue; + if ((len + nOffset) >= (TAS2557_PAGE_REG(nReg) + 4)) { + n += (TAS2557_PAGE_REG(nReg) - nOffset); + nCoefficient = ((int)pCommands[n] << 24) + | ((int)pCommands[n + 1] << 16) + | ((int)pCommands[n + 2] << 8) + | (int)pCommands[n + 3]; + bFound = true; + break; + } + } else { + dev_err(pTAS2557->dev, "%s, format error %d\n", __func__, nOffset); + break; + } + } + + if (bFound) { + *pnValue = nCoefficient; + dev_dbg(pTAS2557->dev, "%s, B[0x%x]P[0x%x]R[0x%x]=0x%x\n", __func__, + TAS2557_BOOK_ID(nReg), TAS2557_PAGE_ID(nReg), TAS2557_PAGE_REG(nReg), + nCoefficient); + } + + return bFound; +} + +static bool tas2557_get_coefficient_in_data(struct tas2557_priv *pTAS2557, + struct TData *pData, int blockType, int nReg, int *pnValue) +{ + bool bFound = false; + struct TBlock *pBlock; + int i; + + for (i = 0; i < pData->mnBlocks; i++) { + pBlock = &(pData->mpBlocks[i]); + if (pBlock->mnType == blockType) { + bFound = tas2557_get_coefficient_in_block(pTAS2557, + pBlock, nReg, pnValue); + if (bFound) + break; + } + } + + return bFound; +} + +static bool tas2557_find_Tmax_in_configuration(struct tas2557_priv *pTAS2557, + struct TConfiguration *pConfiguration, int *pnTMax) +{ + struct TData *pData; + bool bFound = false; + int nBlockType, nReg, nCoefficient; + + if (pTAS2557->mnLPGID == TAS2557_PG_VERSION_2P1) + nReg = TAS2557_PG2P1_CALI_T_REG; + else + nReg = TAS2557_PG1P0_CALI_T_REG; + + if (pConfiguration->mnDevices & channel_left) + nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_A; + else + nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_B; + + pData = &(pConfiguration->mData); + bFound = tas2557_get_coefficient_in_data(pTAS2557, pData, nBlockType, nReg, &nCoefficient); + if (bFound) + *pnTMax = nCoefficient; + + return bFound; +} + +void tas2557_fw_ready(const struct firmware *pFW, void *pContext) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *) pContext; + struct TConfiguration *pConfiguration; + int nResult; + unsigned int nProgram = 0; + unsigned int nSampleRate = 0; + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_lock(&pTAS2557->codec_lock); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_lock(&pTAS2557->file_lock); +#endif + + dev_info(pTAS2557->dev, "%s:\n", __func__); + + if (unlikely(!pFW) || unlikely(!pFW->data)) { + dev_err(pTAS2557->dev, "%s firmware is not loaded.\n", + TAS2557_FW_NAME); + goto end; + } + + if (pTAS2557->mpFirmware->mpConfigurations) { + nProgram = pTAS2557->mnCurrentProgram; + nSampleRate = pTAS2557->mnCurrentSampleRate; + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + dev_dbg(pTAS2557->dev, "clear current firmware\n"); + tas2557_clear_firmware(pTAS2557->mpFirmware); + } + + nResult = fw_parse(pTAS2557, pTAS2557->mpFirmware, (unsigned char *)(pFW->data), pFW->size); + release_firmware(pFW); + if (nResult < 0) { + dev_err(pTAS2557->dev, "firmware is corrupt\n"); + goto end; + } + + if (!pTAS2557->mpFirmware->mnPrograms) { + dev_err(pTAS2557->dev, "firmware contains no programs\n"); + nResult = -EINVAL; + goto end; + } + + if (!pTAS2557->mpFirmware->mnConfigurations) { + dev_err(pTAS2557->dev, "firmware contains no configurations\n"); + nResult = -EINVAL; + goto end; + } + + if (nProgram >= pTAS2557->mpFirmware->mnPrograms) { + dev_info(pTAS2557->dev, + "no previous program, set to default\n"); + nProgram = 0; + } + + pTAS2557->mnCurrentSampleRate = nSampleRate; + nResult = tas2557_set_program(pTAS2557, nProgram, -1); + +end: + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_unlock(&pTAS2557->codec_lock); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_unlock(&pTAS2557->file_lock); +#endif +} + +int tas2557_set_program(struct tas2557_priv *pTAS2557, + unsigned int nProgram, int nConfig) +{ + struct TProgram *pProgram; + struct TConfiguration *pConfiguration; + unsigned int nConfiguration = 0; + unsigned int nSampleRate = 0; + unsigned char nGain; + bool bFound = false; + int nResult = 0; + + if ((!pTAS2557->mpFirmware->mpPrograms) || + (!pTAS2557->mpFirmware->mpConfigurations)) { + dev_err(pTAS2557->dev, "%s: Firmware not loaded\n", __func__); + nResult = 0; + goto end; + } + + if (nProgram >= pTAS2557->mpFirmware->mnPrograms) { + dev_err(pTAS2557->dev, "TAS2557: Program %d doesn't exist\n", + nProgram); + nResult = 0; + goto end; + } + + if (nConfig < 0) { + nConfiguration = 0; + nSampleRate = pTAS2557->mnCurrentSampleRate; + while (!bFound && (nConfiguration < pTAS2557->mpFirmware->mnConfigurations)) { + if (pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnProgram == nProgram) { + if (nSampleRate == 0) { + bFound = true; + dev_info(pTAS2557->dev, "find default configuration %d\n", nConfiguration); + } else if (nSampleRate == pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate) { + bFound = true; + dev_info(pTAS2557->dev, "find matching configuration %d\n", nConfiguration); + } else { + nConfiguration++; + } + } else { + nConfiguration++; + } + } + if (!bFound) { + dev_err(pTAS2557->dev, + "Program %d, no valid configuration found for sample rate %d, ignore\n", + nProgram, nSampleRate); + nResult = 0; + goto end; + } + } else { + if (pTAS2557->mpFirmware->mpConfigurations[nConfig].mnProgram != nProgram) { + dev_err(pTAS2557->dev, "%s, configuration program doesn't match\n", __func__); + nResult = 0; + goto end; + } + nConfiguration = nConfig; + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[nProgram]); + if (pTAS2557->mbPowerUp) { + dev_info(pTAS2557->dev, + "device powered up, power down to load program %d (%s)\n", + nProgram, pProgram->mpName); + if (hrtimer_active(&pTAS2557->mtimer)) + hrtimer_cancel(&pTAS2557->mtimer); + + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) + pTAS2557->enableIRQ(pTAS2557, channel_both, false); + + nResult = tas2557_dev_load_data(pTAS2557, channel_both, p_tas2557_shutdown_data); + /* even if shutdown sequence fails, we still have the chance to recover after hardware reset */ + } + + pTAS2557->hw_reset(pTAS2557); + nResult = pTAS2557->write(pTAS2557, channel_both, TAS2557_SW_RESET_REG, 0x01); + if (nResult < 0) + goto end; + msleep(1); + nResult = tas2557_load_default(pTAS2557); + if (nResult < 0) + goto end; + + dev_info(pTAS2557->dev, "load program %d (%s)\n", nProgram, pProgram->mpName); + nResult = tas2557_load_data(pTAS2557, &(pProgram->mData), TAS2557_BLOCK_PGM_ALL); + if (nResult < 0) + goto end; + nResult = tas2557_load_data(pTAS2557, &(pProgram->mData), TAS2557_BLOCK_PGM_DEV_A); + if (nResult < 0) + goto end; + nResult = tas2557_load_data(pTAS2557, &(pProgram->mData), TAS2557_BLOCK_PGM_DEV_B); + if (nResult < 0) + goto end; + pTAS2557->mnCurrentProgram = nProgram; + + nResult = tas2557_get_DAC_gain(pTAS2557, channel_left, &nGain); + if (nResult < 0) + goto end; + pTAS2557->mnDevGain = nGain; + pTAS2557->mnDevCurrentGain = nGain; + pTAS2557->mnVBoostState = TAS2557_VBST_NEED_DEFAULT; + + nResult = tas2557_load_coefficient(pTAS2557, -1, nConfiguration, false); + if (nResult < 0) + goto end; + + if (pTAS2557->mbPowerUp) { + dev_info(pTAS2557->dev, "%s, load VBoost before power on %d\n", __func__, pTAS2557->mnVBoostState); + nResult = tas2557_set_VBoost(pTAS2557, pTAS2557->mnVBoostState, false); + if (nResult < 0) + goto end; + pTAS2557->clearIRQ(pTAS2557); + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + dev_dbg(pTAS2557->dev, "%s, %s, load startup %d\n", + __func__, pConfiguration->mpName, pConfiguration->mnDevices); + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_startup_data); + if (nResult < 0) + goto end; + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + nResult = tas2557_checkPLL(pTAS2557); + if (nResult < 0) { + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_shutdown_data); + pTAS2557->mbPowerUp = false; + goto end; + } + } + dev_dbg(pTAS2557->dev, "device powered up, load unmute\n"); + nResult = tas2557_dev_load_data(pTAS2557, pConfiguration->mnDevices, p_tas2557_unmute_data); + if (nResult < 0) + goto end; + + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + pTAS2557->enableIRQ(pTAS2557, pConfiguration->mnDevices, true); + if (!hrtimer_active(&pTAS2557->mtimer)) { + pTAS2557->mnDieTvReadCounter = 0; + hrtimer_start(&pTAS2557->mtimer, + ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + } + } + +end: + + if (nResult < 0) { + if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_DEVB_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK)) + failsafe(pTAS2557); + } + return nResult; +} + +int tas2557_set_calibration(struct tas2557_priv *pTAS2557, int nCalibration) +{ + struct TCalibration *pCalibration = NULL; + struct TConfiguration *pConfiguration; + struct TProgram *pProgram; + int nTmax = 0; + bool bFound = false; + int nResult = 0; + + if ((!pTAS2557->mpFirmware->mpPrograms) + || (!pTAS2557->mpFirmware->mpConfigurations)) { + dev_err(pTAS2557->dev, "%s: Firmware not loaded\n\r", __func__); + nResult = 0; + goto end; + } + + if (nCalibration == 0x00FF) { + dev_info(pTAS2557->dev, "load new calibration file %s\n", TAS2557_CAL_NAME); + nResult = tas2557_load_calibration(pTAS2557, TAS2557_CAL_NAME); + if (nResult < 0) + goto end; + nCalibration = 0; + } + + if (nCalibration >= pTAS2557->mpCalFirmware->mnCalibrations) { + dev_err(pTAS2557->dev, + "Calibration %d doesn't exist\n", nCalibration); + nResult = 0; + goto end; + } + + pTAS2557->mnCurrentCalibration = nCalibration; + if (pTAS2557->mbLoadConfigurationPrePowerUp) + goto end; + + pCalibration = &(pTAS2557->mpCalFirmware->mpCalibrations[nCalibration]); + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) { + if (pTAS2557->mbBypassTMax) { + bFound = tas2557_find_Tmax_in_configuration(pTAS2557, pConfiguration, &nTmax); + if (bFound && (nTmax == TAS2557_COEFFICIENT_TMAX)) { + dev_dbg(pTAS2557->dev, "%s, config[%s] bypass load calibration\n", + __func__, pConfiguration->mpName); + goto end; + } + } + + dev_dbg(pTAS2557->dev, "Enable: load calibration\n"); + if (pConfiguration->mnDevices & channel_left) { + nResult = tas2557_load_data(pTAS2557, &(pCalibration->mData), TAS2557_BLOCK_CFG_COEFF_DEV_A); + if (nResult < 0) + goto end; + } + if (pConfiguration->mnDevices & channel_right) + nResult = tas2557_load_data(pTAS2557, &(pCalibration->mData), TAS2557_BLOCK_CFG_COEFF_DEV_B); + } + +end: + if (nResult < 0) { + tas2557_clear_firmware(pTAS2557->mpCalFirmware); + nResult = tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration); + } + + return nResult; +} + +bool tas2557_get_Cali_prm_r0(struct tas2557_priv *pTAS2557, enum channel chl, int *prm_r0) +{ + struct TCalibration *pCalibration; + struct TData *pData; + int nReg; + int nCali_Re; + bool bFound = false; + int nBlockType; + + if (!pTAS2557->mpCalFirmware->mnCalibrations) { + dev_err(pTAS2557->dev, "%s, no calibration data\n", __func__); + goto end; + } + + if (pTAS2557->mnLPGID == TAS2557_PG_VERSION_2P1) + nReg = TAS2557_PG2P1_CALI_R0_REG; + else + nReg = TAS2557_PG1P0_CALI_R0_REG; + + if (chl == channel_left) + nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_A; + else + nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_B; + + pCalibration = &(pTAS2557->mpCalFirmware->mpCalibrations[pTAS2557->mnCurrentCalibration]); + pData = &(pCalibration->mData); + + bFound = tas2557_get_coefficient_in_data(pTAS2557, pData, nBlockType, nReg, &nCali_Re); + +end: + + if (bFound) + *prm_r0 = nCali_Re; + + return bFound; +} + +int tas2557_parse_dt(struct device *dev, struct tas2557_priv *pTAS2557) +{ + struct device_node *np = dev->of_node; + int rc = 0, ret = 0; + unsigned int value; + + pTAS2557->mnLeftChlGpioRst = of_get_named_gpio(np, "ti,reset-gpio-left", 0); + if (!gpio_is_valid(pTAS2557->mnLeftChlGpioRst)) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio-left", np->full_name, pTAS2557->mnLeftChlGpioRst); + else + dev_dbg(pTAS2557->dev, "%s, left reset gpio %d\n", __func__, pTAS2557->mnLeftChlGpioRst); + + pTAS2557->mnRightChlGpioRst = of_get_named_gpio(np, "ti,reset-gpio-right", 0); + if (!gpio_is_valid(pTAS2557->mnRightChlGpioRst)) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio-right", np->full_name, pTAS2557->mnRightChlGpioRst); + else + dev_dbg(pTAS2557->dev, "%s, right reset gpio %d\n", __func__, pTAS2557->mnRightChlGpioRst); + + pTAS2557->mnLeftChlGpioINT = of_get_named_gpio(np, "ti,irq-gpio-left", 0); + if (!gpio_is_valid(pTAS2557->mnLeftChlGpioINT)) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio-left", np->full_name, pTAS2557->mnLeftChlGpioINT); + + pTAS2557->mnRightChlGpioINT = of_get_named_gpio(np, "ti,irq-gpio-right", 0); + if (!gpio_is_valid(pTAS2557->mnRightChlGpioINT)) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio-right", np->full_name, pTAS2557->mnRightChlGpioINT); + + rc = of_property_read_u32(np, "ti,left-channel", &value); + if (rc) { + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,left-channel", np->full_name, rc); + ret = -EINVAL; + goto end; + } else { + pTAS2557->mnLAddr = value; + dev_dbg(pTAS2557->dev, "ti,left-channel=0x%x\n", pTAS2557->mnLAddr); + } + + rc = of_property_read_u32(np, "ti,right-channel", &value); + if (rc) { + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,right-channel", np->full_name, rc); + ret = -EINVAL; + goto end; + } else { + pTAS2557->mnRAddr = value; + dev_dbg(pTAS2557->dev, "ti,right-channel=0x%x\n", pTAS2557->mnRAddr); + } + + rc = of_property_read_u32(np, "ti,echo-ref", &value); + if (rc) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,echo-ref", np->full_name, rc); + else + pTAS2557->mnEchoRef = value; + + rc = of_property_read_u32(np, "ti,i2s-bits", &value); + if (rc) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,i2s-bits", np->full_name, rc); + else + pTAS2557->mnI2SBits = value; + + rc = of_property_read_u32(np, "ti,bypass-tmax", &value); + if (rc) + dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n", + "ti,bypass-tmax", np->full_name, rc); + else + pTAS2557->mbBypassTMax = (value > 0); + +end: + + return ret; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2557 common functions for Android Linux"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-core.h b/sound/soc/codecs/tas2557_stereo/tas2557-core.h new file mode 100644 index 00000000000000..85a724dccd3a44 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-core.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-core.h + * Description: header file for tas2557-core.c + */ + +#ifndef _TAS2557_CORE_H +#define _TAS2557_CORE_H + +#include "tas2557.h" + +#define TAS2557_YRAM_BOOK1 140 + +#define TAS2557_YRAM1_PAGE 42 +#define TAS2557_YRAM1_START_REG 88 +#define TAS2557_YRAM1_END_REG 127 + +#define TAS2557_YRAM2_START_PAGE 43 +#define TAS2557_YRAM2_END_PAGE 49 +#define TAS2557_YRAM2_START_REG 8 +#define TAS2557_YRAM2_END_REG 127 + +#define TAS2557_YRAM3_PAGE 50 +#define TAS2557_YRAM3_START_REG 8 +#define TAS2557_YRAM3_END_REG 27 + +/* should not include B0_P53_R44-R47 */ +#define TAS2557_YRAM_BOOK2 0 +#define TAS2557_YRAM4_START_PAGE 50 +#define TAS2557_YRAM4_END_PAGE 60 +#define TAS2557_YRAM4_START_REG 8 +#define TAS2557_YRAM4_END_REG 127 + +#define TAS2557_YRAM5_PAGE 61 +#define TAS2557_YRAM5_START_REG 8 +#define TAS2557_YRAM5_END_REG 27 + +#define TAS2557_COEFFICIENT_TMAX 0x7fffffff +#define TAS2557_SAFE_GUARD_PATTERN 0x5a +#define LOW_TEMPERATURE_CHECK_PERIOD 5000 /* 5 second */ + +struct TYCRC { + unsigned char mnOffset; + unsigned char mnLen; +}; + +int tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable); +int tas2557_SA_DevChnSetup(struct tas2557_priv *pTAS2557, unsigned int mode); +int tas2557_set_VBoost(struct tas2557_priv *pTAS2557, int vboost, bool bPowerUp); +int tas2557_get_VBoost(struct tas2557_priv *pTAS2557, int *pVBoost); +int tas2557_SA_ctl_echoRef(struct tas2557_priv *pTAS2557); +int tas2557_get_die_temperature(struct tas2557_priv *pTAS2557, int *pTemperature); +int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSamplingRate); +int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, enum channel chn, unsigned int nBitRate); +int tas2557_get_bit_rate(struct tas2557_priv *pTAS2557, enum channel chn, unsigned char *pBitRate); +int tas2557_set_config(struct tas2557_priv *pTAS2557, int config); +void tas2557_fw_ready(const struct firmware *pFW, void *pContext); +bool tas2557_get_Cali_prm_r0(struct tas2557_priv *pTAS2557, enum channel chl, int *prm_r0); +int tas2557_set_program(struct tas2557_priv *pTAS2557, unsigned int nProgram, int nConfig); +int tas2557_set_calibration(struct tas2557_priv *pTAS2557, int nCalibration); +int tas2557_load_default(struct tas2557_priv *pTAS2557); +int tas2557_parse_dt(struct device *dev, struct tas2557_priv *pTAS2557); +int tas2557_get_DAC_gain(struct tas2557_priv *pTAS2557, enum channel chl, unsigned char *pnGain); +int tas2557_set_DAC_gain(struct tas2557_priv *pTAS2557, enum channel chl, unsigned int nGain); +int tas2557_configIRQ(struct tas2557_priv *pTAS2557); +#endif /* _TAS2557_CORE_H */ diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-misc.c b/sound/soc/codecs/tas2557_stereo/tas2557-misc.c new file mode 100644 index 00000000000000..852e49661ec8e1 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-misc.c @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-misc.c + * Description: misc driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier + */ + +#ifdef CONFIG_TAS2557_MISC_STEREO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2557.h" +#include "tas2557-core.h" +#include "tas2557-misc.h" +#include + +static int g_logEnable = 0; +static struct tas2557_priv *g_tas2557; + +static int tas2557_file_open(struct inode *inode, struct file *file) +{ + struct tas2557_priv *pTAS2557 = g_tas2557; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)pTAS2557; + if (g_logEnable) + dev_info(pTAS2557->dev, "%s\n", __func__); + return 0; +} + +static int tas2557_file_release(struct inode *inode, struct file *file) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data; + + if (g_logEnable) + dev_info(pTAS2557->dev, "%s\n", __func__); + file->private_data = (void *)NULL; + module_put(THIS_MODULE); + + return 0; +} + +static ssize_t tas2557_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data; + int ret = 0; + unsigned int nValue = 0; + unsigned char value = 0; + unsigned char *p_kBuf = NULL; + + mutex_lock(&pTAS2557->file_lock); + switch (pTAS2557->mnDBGCmd) { + case TIAUDIO_CMD_REG_READ: { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_REG_READ: chn[%d], current_reg = 0x%x, count=%d\n", + pTAS2557->mnCurrentChannel, pTAS2557->mnCurrentReg, (int)count); + if (count == 1) { + ret = pTAS2557->read(pTAS2557, pTAS2557->mnCurrentChannel, + pTAS2557->mnCurrentReg, &nValue); + if (ret < 0) { + dev_err(pTAS2557->dev, "dev read fail %d\n", ret); + break; + } + value = (u8)nValue; + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", nValue, value); + ret = copy_to_user(buf, &value, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + } else if (count > 1) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + ret = pTAS2557->bulk_read(pTAS2557, pTAS2557->mnCurrentChannel, + pTAS2557->mnCurrentReg, p_kBuf, count); + if (ret < 0) { + dev_err(pTAS2557->dev, "dev bulk read fail %d\n", ret); + } else { + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + } + kfree(p_kBuf); + } else { + dev_err(pTAS2557->dev, "read no mem\n"); + } + } + } + break; + case TIAUDIO_CMD_PROGRAM: { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_PROGRAM: count = %d\n", (int)count); + + if (count == PROGRAM_BUF_SIZE) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + struct TProgram *pProgram = + &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + p_kBuf[0] = pTAS2557->mpFirmware->mnPrograms; + p_kBuf[1] = pTAS2557->mnCurrentProgram; + p_kBuf[2] = pProgram->mnAppMode; + p_kBuf[3] = (pProgram->mnBoost&0xff00)>>8; + p_kBuf[4] = (pProgram->mnBoost&0x00ff); + memcpy(&p_kBuf[5], pProgram->mpName, FW_NAME_SIZE); + strlcpy(&p_kBuf[5+FW_NAME_SIZE], pProgram->mpDescription, strlen(pProgram->mpDescription) + 1); + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + kfree(p_kBuf); + } else + dev_err(pTAS2557->dev, "read no mem\n"); + } else + dev_err(pTAS2557->dev, "read buffer not sufficient\n"); + } else + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + } + break; + case TIAUDIO_CMD_CONFIGURATION: { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_CONFIGURATION: count = %d\n", (int)count); + if (count == CONFIGURATION_BUF_SIZE) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + struct TConfiguration *pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + + p_kBuf[0] = pTAS2557->mpFirmware->mnConfigurations; + p_kBuf[1] = pTAS2557->mnCurrentConfiguration; + memcpy(&p_kBuf[2], pConfiguration->mpName, FW_NAME_SIZE); + p_kBuf[2+FW_NAME_SIZE] = pConfiguration->mnProgram; + p_kBuf[3+FW_NAME_SIZE] = pConfiguration->mnPLL; + p_kBuf[4+FW_NAME_SIZE] = (pConfiguration->mnSamplingRate&0x000000ff); + p_kBuf[5+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8); + p_kBuf[6+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16); + p_kBuf[7+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0xff000000)>>24); + strlcpy(&p_kBuf[8+FW_NAME_SIZE], pConfiguration->mpDescription, strlen(pConfiguration->mpDescription)+1); + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + kfree(p_kBuf); + } else + dev_err(pTAS2557->dev, "read no mem\n"); + } else + dev_err(pTAS2557->dev, "read buffer not sufficient\n"); + } else + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + } + break; + case TIAUDIO_CMD_FW_TIMESTAMP: + { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_FW_TIMESTAMP: count = %d\n", (int)count); + + if (count == 4) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + p_kBuf[0] = (pTAS2557->mpFirmware->mnTimeStamp&0x000000ff); + p_kBuf[1] = ((pTAS2557->mpFirmware->mnTimeStamp&0x0000ff00)>>8); + p_kBuf[2] = ((pTAS2557->mpFirmware->mnTimeStamp&0x00ff0000)>>16); + p_kBuf[3] = ((pTAS2557->mpFirmware->mnTimeStamp&0xff000000)>>24); + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + kfree(p_kBuf); + } else { + dev_err(pTAS2557->dev, "read no mem\n"); + } + } + } + break; + + case TIAUDIO_CMD_CALIBRATION: { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_CALIBRATION: count = %d\n", + (int)count); + + if (count == 1) { + unsigned char curCal = pTAS2557->mnCurrentCalibration; + + ret = copy_to_user(buf, &curCal, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + } + } + break; + + case TIAUDIO_CMD_SAMPLERATE: + { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_SAMPLERATE: count = %d\n", + (int)count); + if (count == 4) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + struct TConfiguration *pConfiguration = + &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + + p_kBuf[0] = (pConfiguration->mnSamplingRate&0x000000ff); + p_kBuf[1] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8); + p_kBuf[2] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16); + p_kBuf[3] = ((pConfiguration->mnSamplingRate&0xff000000)>>24); + + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + + kfree(p_kBuf); + } else { + dev_err(pTAS2557->dev, "read no mem\n"); + } + } + } + break; + + case TIAUDIO_CMD_BITRATE: + { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_BITRATE: count = %d\n", (int)count); + + if (count == 1) { + unsigned char bitRate = 0; + + tas2557_get_bit_rate(pTAS2557, pTAS2557->mnCurrentChannel, &bitRate); + ret = copy_to_user(buf, &bitRate, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + } + } + break; + + case TIAUDIO_CMD_DACVOLUME: + { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_DACVOLUME: count = %d\n", + (int)count); + + if (count == 1) { + unsigned char volume = 0; + + ret = tas2557_get_DAC_gain(pTAS2557, pTAS2557->mnCurrentChannel, &volume); + if (ret >= 0) { + ret = copy_to_user(buf, &volume, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2557->dev, "copy to user fail %d\n", ret); + } + } + } + } + break; + } + pTAS2557->mnDBGCmd = 0; + + mutex_unlock(&pTAS2557->file_lock); + return count; +} + +static ssize_t tas2557_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data; + int ret = 0; + unsigned char *p_kBuf = NULL; + unsigned int reg = 0; + enum channel chl; + unsigned int len = 0; + + mutex_lock(&pTAS2557->file_lock); + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf == NULL) { + dev_err(pTAS2557->dev, "write no mem\n"); + goto err; + } + ret = copy_from_user(p_kBuf, buf, count); + if (ret != 0) { + dev_err(pTAS2557->dev, "copy_from_user failed.\n"); + goto err; + } + + pTAS2557->mnDBGCmd = p_kBuf[0]; + switch (pTAS2557->mnDBGCmd) { + case TIAUDIO_CMD_REG_WITE: + if (count > 6) { + chl = p_kBuf[1]; + reg = ((unsigned int)p_kBuf[2] << 24) + + ((unsigned int)p_kBuf[3] << 16) + + ((unsigned int)p_kBuf[4] << 8) + + (unsigned int)p_kBuf[5]; + len = count - 6; + if (len == 1) { + ret = pTAS2557->write(pTAS2557, + chl, reg, p_kBuf[6]); + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_REG_WITE,chn[%d], Reg=0x%x, Val=0x%x\n", + chl, reg, p_kBuf[6]); + } else { + ret = pTAS2557->bulk_write(pTAS2557, + chl, reg, &p_kBuf[6], len); + } + } else { + dev_err(pTAS2557->dev, "%s, write len fail, count=%d.\n", + __func__, (int)count); + } + pTAS2557->mnDBGCmd = 0; + break; + + case TIAUDIO_CMD_REG_READ: + if (count == 6) { + pTAS2557->mnCurrentChannel = p_kBuf[1]; + pTAS2557->mnCurrentReg = ((unsigned int)p_kBuf[2] << 24) + + ((unsigned int)p_kBuf[3] << 16) + + ((unsigned int)p_kBuf[4] << 8) + + (unsigned int)p_kBuf[5]; + if (g_logEnable) { + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_REG_READ chl[%d], whole=0x%x\n", + pTAS2557->mnCurrentChannel, + pTAS2557->mnCurrentReg); + } + } else { + dev_err(pTAS2557->dev, "read len fail.\n"); + } + break; + + case TIAUDIO_CMD_DEBUG_ON: + { + if (count == 2) + g_logEnable = p_kBuf[1]; + + pTAS2557->mnDBGCmd = 0; + } + break; + + case TIAUDIO_CMD_PROGRAM: + { + if (count == 2) { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) { + int config = -1; + + if (p_kBuf[1] == pTAS2557->mnCurrentProgram) + config = pTAS2557->mnCurrentConfiguration; + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_PROGRAM, set to %d, cfg=%d\n", p_kBuf[1], config); + tas2557_set_program(pTAS2557, p_kBuf[1], config); + pTAS2557->mnDBGCmd = 0; + } else + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + } + } + break; + + case TIAUDIO_CMD_CONFIGURATION: + { + if (count == 2) { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_CONFIGURATION, set to %d\n", p_kBuf[1]); + tas2557_set_config(pTAS2557, p_kBuf[1]); + pTAS2557->mnDBGCmd = 0; + } else + dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + } + } + break; + + case TIAUDIO_CMD_FW_TIMESTAMP: + /*let go*/ + break; + + case TIAUDIO_CMD_CALIBRATION: + { + if (count == 2) { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_CALIBRATION, set to %d\n", p_kBuf[1]); + tas2557_set_calibration(pTAS2557, p_kBuf[1]); + pTAS2557->mnDBGCmd = 0; + } + } + } + break; + + case TIAUDIO_CMD_SAMPLERATE: + { + if (count == 5) { + unsigned int nSampleRate = ((unsigned int)p_kBuf[1] << 24) + + ((unsigned int)p_kBuf[2] << 16) + + ((unsigned int)p_kBuf[3] << 8) + + (unsigned int)p_kBuf[4]; + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_SAMPLERATE, set to %d\n", nSampleRate); + + tas2557_set_sampling_rate(pTAS2557, nSampleRate); + } + } + break; + + case TIAUDIO_CMD_BITRATE: + { + pTAS2557->mnCurrentChannel = p_kBuf[1]; + if (count == 3) { + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_BITRATE, set to %d\n", p_kBuf[1]); + + tas2557_set_bit_rate(pTAS2557, pTAS2557->mnCurrentChannel, p_kBuf[2]); + } + } + break; + + case TIAUDIO_CMD_DACVOLUME: + { + pTAS2557->mnCurrentChannel = p_kBuf[1]; + if (count == 3) { + unsigned char volume; + + volume = (p_kBuf[2] & 0x0f); + if (g_logEnable) + dev_info(pTAS2557->dev, "TIAUDIO_CMD_DACVOLUME, set to %d\n", volume); + + tas2557_set_DAC_gain(pTAS2557, pTAS2557->mnCurrentChannel, volume); + } + } + break; + + case TIAUDIO_CMD_SPEAKER: + { + if (count == 2) { + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_SPEAKER, set to %d\n", + p_kBuf[1]); + tas2557_enable(pTAS2557, (p_kBuf[1] > 0)); + } + } + break; + + case TIAUDIO_CMD_FW_RELOAD: + { + if (count == 1) { + ret = request_firmware_nowait(THIS_MODULE, 1, TAS2557_FW_NAME, + pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready); + + if (g_logEnable) + dev_info(pTAS2557->dev, + "TIAUDIO_CMD_FW_RELOAD: ret = %d\n", + ret); + } + } + break; + + case TIAUDIO_CMD_SUSPEND: + { + if (count == 2) { + bool bSuspend = (p_kBuf[1] != 0); + + if (bSuspend) + pTAS2557->runtime_suspend(pTAS2557); + else + pTAS2557->runtime_resume(pTAS2557); + } + } + break; + + default: + pTAS2557->mnDBGCmd = 0; + break; + } + +err: + if (p_kBuf != NULL) + kfree(p_kBuf); + + mutex_unlock(&pTAS2557->file_lock); + + return count; +} + +static long tas2557_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct tas2557_priv *pTAS2557 = file->private_data; + int ret = 0; + + mutex_lock(&pTAS2557->file_lock); + + switch (cmd) { + case SMARTPA_SPK_DAC_VOLUME: + { + } + break; + + case SMARTPA_SPK_POWER_ON: + { + tas2557_enable(pTAS2557, true); + } + break; + + case SMARTPA_SPK_POWER_OFF: + { + tas2557_enable(pTAS2557, false); + } + break; + + case SMARTPA_SPK_SWITCH_PROGRAM: + { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) + tas2557_set_program(pTAS2557, arg, -1); + } + break; + + case SMARTPA_SPK_SWITCH_CONFIGURATION: + { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) + tas2557_set_config(pTAS2557, arg); + } + break; + + case SMARTPA_SPK_SWITCH_CALIBRATION: + { + if ((pTAS2557->mpFirmware->mnConfigurations > 0) + && (pTAS2557->mpFirmware->mnPrograms > 0)) + tas2557_set_calibration(pTAS2557, arg); + } + break; + + case SMARTPA_SPK_SET_SAMPLERATE: + { + tas2557_set_sampling_rate(pTAS2557, arg); + } + break; + + case SMARTPA_SPK_SET_BITRATE: + { + tas2557_set_bit_rate(pTAS2557, channel_both, arg); + } + break; + } + + mutex_unlock(&pTAS2557->file_lock); + return ret; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = tas2557_file_read, + .write = tas2557_file_write, + .unlocked_ioctl = tas2557_file_unlocked_ioctl, + .open = tas2557_file_open, + .release = tas2557_file_release, +}; + +#define MODULE_NAME "tas2557s" +static struct miscdevice tas2557_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MODULE_NAME, + .fops = &fops, +}; + +int tas2557_register_misc(struct tas2557_priv *pTAS2557) +{ + int ret = 0; + + g_tas2557 = pTAS2557; + + ret = misc_register(&tas2557_misc); + if (ret) + dev_err(pTAS2557->dev, "TAS2557 misc fail: %d\n", ret); + + dev_info(pTAS2557->dev, "%s, leave\n", __func__); + + return ret; +} + +int tas2557_deregister_misc(struct tas2557_priv *pTAS2557) +{ + misc_deregister(&tas2557_misc); + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2557 Misc Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-misc.h b/sound/soc/codecs/tas2557_stereo/tas2557-misc.h new file mode 100644 index 00000000000000..80da9be3bf3b63 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-misc.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-misc.h + * Description: header file for tas2557-misc.c + */ + +#ifndef _TAS2557_MISC_H +#define _TAS2557_MISC_H + +#define FW_NAME_SIZE 64 +#define FW_DESCRIPTION_SIZE 256 +#define PROGRAM_BUF_SIZE (5 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE) +#define CONFIGURATION_BUF_SIZE (8 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE) + +#define TIAUDIO_CMD_REG_WITE 1 +#define TIAUDIO_CMD_REG_READ 2 +#define TIAUDIO_CMD_DEBUG_ON 3 +#define TIAUDIO_CMD_PROGRAM 4 +#define TIAUDIO_CMD_CONFIGURATION 5 +#define TIAUDIO_CMD_FW_TIMESTAMP 6 +#define TIAUDIO_CMD_CALIBRATION 7 +#define TIAUDIO_CMD_SAMPLERATE 8 +#define TIAUDIO_CMD_BITRATE 9 +#define TIAUDIO_CMD_DACVOLUME 10 +#define TIAUDIO_CMD_SPEAKER 11 +#define TIAUDIO_CMD_FW_RELOAD 12 +#define TIAUDIO_CMD_SUSPEND 13 + +#define TAS2557_MAGIC_NUMBER 0x3537 /* '2557' */ + +#define SMARTPA_SPK_DAC_VOLUME _IOWR(TAS2557_MAGIC_NUMBER, 1, unsigned long) +#define SMARTPA_SPK_POWER_ON _IOWR(TAS2557_MAGIC_NUMBER, 2, unsigned long) +#define SMARTPA_SPK_POWER_OFF _IOWR(TAS2557_MAGIC_NUMBER, 3, unsigned long) +#define SMARTPA_SPK_SWITCH_PROGRAM _IOWR(TAS2557_MAGIC_NUMBER, 4, unsigned long) +#define SMARTPA_SPK_SWITCH_CONFIGURATION _IOWR(TAS2557_MAGIC_NUMBER, 5, unsigned long) +#define SMARTPA_SPK_SWITCH_CALIBRATION _IOWR(TAS2557_MAGIC_NUMBER, 6, unsigned long) +#define SMARTPA_SPK_SET_SAMPLERATE _IOWR(TAS2557_MAGIC_NUMBER, 7, unsigned long) +#define SMARTPA_SPK_SET_BITRATE _IOWR(TAS2557_MAGIC_NUMBER, 8, unsigned long) + +int tas2557_register_misc(struct tas2557_priv *pTAS2557); +int tas2557_deregister_misc(struct tas2557_priv *pTAS2557); + +#endif /* _TAS2557_MISC_H */ diff --git a/sound/soc/codecs/tas2557_stereo/tas2557-regmap.c b/sound/soc/codecs/tas2557_stereo/tas2557-regmap.c new file mode 100644 index 00000000000000..33c0a56d338760 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557-regmap.c @@ -0,0 +1,1362 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557-regmap.c + * Description: I2C driver with regmap for Texas Instruments TAS2557 High Performance 4W Smart Amplifier + */ + +#ifdef CONFIG_TAS2557_REGMAP_STEREO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tas2557.h" +#include "tas2557-core.h" + +#ifdef CONFIG_TAS2557_CODEC_STEREO +#include "tas2557-codec.h" +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO +#include "tas2557-misc.h" +#endif + +#define ENABLE_TILOAD +#ifdef ENABLE_TILOAD +#include "tiload.h" +#endif + +#define LOW_TEMPERATURE_GAIN 6 +#define LOW_TEMPERATURE_COUNTER 12 + +/* + * tas2557_i2c_write_device : write single byte to device + * platform dependent, need platform specific support + */ +static int tas2557_i2c_write_device( + struct tas2557_priv *pTAS2557, + unsigned char addr, + unsigned char reg, + unsigned char value) +{ + int ret = 0; + + pTAS2557->client->addr = addr; + ret = regmap_write(pTAS2557->mpRegmap, reg, value); + if (ret < 0) { + dev_err(pTAS2557->dev, "%s[0x%x] Error, Reg=0x%x, value=0x%x, ret =%d\n", + __func__, addr, reg, value, ret); + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode |= ERROR_DEVB_I2C_COMM; + } else { + ret = 1; + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVB_I2C_COMM; + } + return ret; +} + +/* + * tas2557_i2c_bulkwrite_device : write multiple bytes to device + * platform dependent, need platform specific support + */ +static int tas2557_i2c_bulkwrite_device( + struct tas2557_priv *pTAS2557, + unsigned char addr, + unsigned char reg, + unsigned char *pBuf, + unsigned int len) +{ + int ret = 0; + + pTAS2557->client->addr = addr; + ret = regmap_bulk_write(pTAS2557->mpRegmap, reg, pBuf, len); + if (ret < 0) { + dev_err(pTAS2557->dev, "%s[0x%x] Error %d\n", + __func__, addr, ret); + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode |= ERROR_DEVB_I2C_COMM; + } else { + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVB_I2C_COMM; + + ret = len; + } + + return ret; +} + +/* + * tas2557_i2c_read_device : read single byte from device + * platform dependent, need platform specific support + */ +static int tas2557_i2c_read_device( + struct tas2557_priv *pTAS2557, + unsigned char addr, + unsigned char reg, + unsigned char *p_value) +{ + int ret = 0; + unsigned int val = 0; + + pTAS2557->client->addr = addr; + ret = regmap_read(pTAS2557->mpRegmap, reg, &val); + if (ret < 0) { + dev_err(pTAS2557->dev, "%s[0x%x] Error %d\n", + __func__, addr, ret); + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode |= ERROR_DEVB_I2C_COMM; + } else { + *p_value = (unsigned char)val; + ret = 1; + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVB_I2C_COMM; + } + + return ret; +} + +/* + * tas2557_i2c_bulkread_device : read multiple bytes from device + * platform dependent, need platform specific support + */ +static int tas2557_i2c_bulkread_device( + struct tas2557_priv *pTAS2557, + unsigned char addr, + unsigned char reg, + unsigned char *p_value, + unsigned int len) +{ + int ret = 0; + + pTAS2557->client->addr = addr; + ret = regmap_bulk_read(pTAS2557->mpRegmap, reg, p_value, len); + + if (ret < 0) { + dev_err(pTAS2557->dev, "%s[0x%x] Error %d\n", + __func__, addr, ret); + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode |= ERROR_DEVB_I2C_COMM; + } else { + ret = len; + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVB_I2C_COMM; + } + + return ret; +} + +static int tas2557_i2c_update_bits( + struct tas2557_priv *pTAS2557, + unsigned char addr, + unsigned char reg, + unsigned char mask, + unsigned char value) +{ + int ret = 0; + + pTAS2557->client->addr = addr; + ret = regmap_update_bits(pTAS2557->mpRegmap, reg, mask, value); + + if (ret < 0) { + dev_err(pTAS2557->dev, "%s[0x%x] Error %d\n", + __func__, addr, ret); + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode |= ERROR_DEVB_I2C_COMM; + } else { + ret = 1; + if (addr == pTAS2557->mnLAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + else if (addr == pTAS2557->mnRAddr) + pTAS2557->mnErrCode &= ~ERROR_DEVB_I2C_COMM; + } + + return ret; +} + +/* tas2557_change_book_page : switch to certain book and page + * platform independent, don't change unless necessary + */ +static int tas2557_change_book_page( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned char nBook, + unsigned char nPage) +{ + int nResult = 0; + + if (chn & channel_left) { + if (pTAS2557->mnLCurrentBook == nBook) { + if (pTAS2557->mnLCurrentPage != nPage) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_BOOKCTL_PAGE, nPage); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentPage = nPage; + } + } else { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_BOOKCTL_PAGE, 0); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentPage = 0; + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_BOOKCTL_REG, nBook); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentBook = nBook; + if (nPage != 0) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_BOOKCTL_PAGE, nPage); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentPage = nPage; + } + } + } + + if (chn & channel_right) { + if (pTAS2557->mnRCurrentBook == nBook) { + if (pTAS2557->mnRCurrentPage != nPage) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_BOOKCTL_PAGE, nPage); + if (nResult < 0) + goto end; + pTAS2557->mnRCurrentPage = nPage; + } + } else { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_BOOKCTL_PAGE, 0); + if (nResult < 0) + goto end; + pTAS2557->mnRCurrentPage = 0; + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_BOOKCTL_REG, nBook); + if (nResult < 0) + goto end; + pTAS2557->mnRCurrentBook = nBook; + if (nPage != 0) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_BOOKCTL_PAGE, nPage); + if (nResult < 0) + goto end; + pTAS2557->mnRCurrentPage = nPage; + } + } + } + + if (chn == channel_broadcast) { + nResult = tas2557_i2c_write_device(pTAS2557, + TAS2557_BROADCAST_ADDR, TAS2557_BOOKCTL_PAGE, 0); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentPage = 0; + pTAS2557->mnRCurrentPage = 0; + nResult = tas2557_i2c_write_device(pTAS2557, + TAS2557_BROADCAST_ADDR, TAS2557_BOOKCTL_REG, nBook); + if (nResult < 0) + goto end; + pTAS2557->mnLCurrentBook = nBook; + pTAS2557->mnRCurrentBook = nBook; + nResult = tas2557_i2c_write_device(pTAS2557, + TAS2557_BROADCAST_ADDR, TAS2557_BOOKCTL_PAGE, nPage); + if (nResult >= 0) { + pTAS2557->mnLCurrentPage = nPage; + pTAS2557->mnRCurrentPage = nPage; + } + } + +end: + + return nResult; +} + +/* tas2557_dev_read : + * platform independent, don't change unless necessary + */ +static int tas2557_dev_read( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int nRegister, + unsigned int *pValue) +{ + int nResult = 0; + unsigned char Value = 0; + + mutex_lock(&pTAS2557->dev_lock); + + if (pTAS2557->mbTILoadActive) { + if (!(nRegister & 0x80000000)) { + /* let only reads from TILoad pass. */ + goto end; + } + nRegister &= ~0x80000000; + + dev_dbg(pTAS2557->dev, "TiLoad R CH[%d] REG B[%d]P[%d]R[%d]\n", + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), + TAS2557_PAGE_REG(nRegister)); + } + + nResult = tas2557_change_book_page(pTAS2557, chn, + TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); + if (nResult < 0) + goto end; + + if (chn == channel_left) + nResult = tas2557_i2c_read_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_PAGE_REG(nRegister), &Value); + else if (chn == channel_right) + nResult = tas2557_i2c_read_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_PAGE_REG(nRegister), &Value); + else { + dev_err(pTAS2557->dev, "read chn ERROR %d\n", chn); + nResult = -1; + } + + if (nResult >= 0) + *pValue = Value; + +end: + mutex_unlock(&pTAS2557->dev_lock); + return nResult; +} + +/* tas2557_dev_write : + * platform independent, don't change unless necessary + */ +static int tas2557_dev_write( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int nRegister, + unsigned int nValue) +{ + int nResult = 0; + + mutex_lock(&pTAS2557->dev_lock); + if ((nRegister == 0xAFFEAFFE) && (nValue == 0xBABEBABE)) { + pTAS2557->mbTILoadActive = true; + dev_dbg(pTAS2557->dev, "TiLoad Active\n"); + goto end; + } + + if ((nRegister == 0xBABEBABE) && (nValue == 0xAFFEAFFE)) { + pTAS2557->mbTILoadActive = false; + dev_dbg(pTAS2557->dev, "TiLoad DeActive\n"); + goto end; + } + + if (pTAS2557->mbTILoadActive) { + if (!(nRegister & 0x80000000)) { + /* let only writes from TILoad pass. */ + goto end; + } + nRegister &= ~0x80000000; + + dev_dbg(pTAS2557->dev, "TiLoad W CH[%d] REG B[%d]P[%d]R[%d] =0x%x\n", + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), + TAS2557_PAGE_REG(nRegister), nValue); + } + + nResult = tas2557_change_book_page(pTAS2557, + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); + if (nResult < 0) + goto end; + + if (chn & channel_left) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnLAddr, TAS2557_PAGE_REG(nRegister), nValue); + if (nResult < 0) + goto end; + } + if (chn & channel_right) { + nResult = tas2557_i2c_write_device(pTAS2557, + pTAS2557->mnRAddr, TAS2557_PAGE_REG(nRegister), nValue); + if (nResult < 0) + goto end; + } + if (chn == channel_broadcast) + nResult = tas2557_i2c_write_device(pTAS2557, + TAS2557_BROADCAST_ADDR, TAS2557_PAGE_REG(nRegister), nValue); + +end: + mutex_unlock(&pTAS2557->dev_lock); + return nResult; +} + +/* tas2557_dev_bulk_read : + * platform independent, don't change unless necessary + */ +static int tas2557_dev_bulk_read( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int nRegister, + u8 *pData, + unsigned int nLength) +{ + int nResult = 0; + unsigned char reg = 0; + unsigned char Addr = 0; + int i; + + mutex_lock(&pTAS2557->dev_lock); + if (pTAS2557->mbTILoadActive) { + if (!(nRegister & 0x80000000)) { + /* let only writes from TILoad pass. */ + goto end; + } + + nRegister &= ~0x80000000; + dev_dbg(pTAS2557->dev, "TiLoad BR CH[%d] REG B[%d]P[%d]R[%d], count=%d\n", + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), + TAS2557_PAGE_REG(nRegister), nLength); + } + + nResult = tas2557_change_book_page(pTAS2557, + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); + if (nResult < 0) + goto end; + + reg = TAS2557_PAGE_REG(nRegister); + if (chn == channel_left) + Addr = pTAS2557->mnLAddr; + else if (chn == channel_right) + Addr = pTAS2557->mnRAddr; + else { + dev_err(pTAS2557->dev, "bulk read chn ERROR %d\n", chn); + nResult = -1; + } + + if (nResult >= 0) { + #define STRIDE 4 + /* Read chunk bytes defined by STRIDE */ + for (i = 0; i < (nLength / STRIDE); i++) { + nResult = regmap_bulk_read(pTAS2557->mpRegmap, + TAS2557_PAGE_REG((nRegister + i*STRIDE)), + &pData[i*STRIDE], STRIDE); + if (nResult < 0) { + dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", + __func__, __LINE__, nResult); + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + } else { + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + } + } + + /* Read remaining bytes */ + if ((nLength % STRIDE) != 0) { + nResult = regmap_bulk_read(pTAS2557->mpRegmap, + TAS2557_PAGE_REG(nRegister + i*STRIDE), + &pData[i*STRIDE], (nLength % STRIDE)); + if (nResult < 0) { + dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", + __func__, __LINE__, nResult); + pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; + } else { + pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; + } + } + } + +end: + mutex_unlock(&pTAS2557->dev_lock); + return nResult; +} + +/* tas2557_dev_bulk_write : + * platform independent, don't change unless necessary + */ +static int tas2557_dev_bulk_write( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int nRegister, + u8 *pData, + unsigned int nLength) +{ + int nResult = 0; + unsigned char reg = 0; + + mutex_lock(&pTAS2557->dev_lock); + if (pTAS2557->mbTILoadActive) { + if (!(nRegister & 0x80000000)) { + /* let only writes from TILoad pass. */ + goto end; + } + + nRegister &= ~0x80000000; + + dev_dbg(pTAS2557->dev, "TiLoad BW CH[%d] REG B[%d]P[%d]R[%d], count=%d\n", + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), + TAS2557_PAGE_REG(nRegister), nLength); + } + + nResult = tas2557_change_book_page(pTAS2557, + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); + + if (nResult >= 0) { + reg = TAS2557_PAGE_REG(nRegister); + if (chn & channel_left) { + nResult = tas2557_i2c_bulkwrite_device(pTAS2557, + pTAS2557->mnLAddr, reg, pData, nLength); + if (nResult < 0) + goto end; + } + + if (chn & channel_right) { + nResult = tas2557_i2c_bulkwrite_device(pTAS2557, + pTAS2557->mnRAddr, reg, pData, nLength); + if (nResult < 0) + goto end; + } + + if (chn == channel_broadcast) + nResult = tas2557_i2c_bulkwrite_device(pTAS2557, TAS2557_BROADCAST_ADDR, reg, pData, nLength); + } + +end: + mutex_unlock(&pTAS2557->dev_lock); + return nResult; +} + +/* tas2557_dev_update_bits : + * platform independent, don't change unless necessary + */ +static int tas2557_dev_update_bits( + struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int nRegister, + unsigned int nMask, + unsigned int nValue) +{ + int nResult = 0; + + mutex_lock(&pTAS2557->dev_lock); + + if (pTAS2557->mbTILoadActive) { + if (!(nRegister & 0x80000000)) { + /* let only writes from TILoad pass. */ + goto end; + } + + nRegister &= ~0x80000000; + dev_dbg(pTAS2557->dev, "TiLoad SB CH[%d] REG B[%d]P[%d]R[%d], mask=0x%x, value=0x%x\n", + chn, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), + TAS2557_PAGE_REG(nRegister), nMask, nValue); + } + + nResult = tas2557_change_book_page(pTAS2557, chn, + TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); + if (nResult < 0) + goto end; + + if (chn & channel_left) { + nResult = tas2557_i2c_update_bits(pTAS2557, + pTAS2557->mnLAddr, TAS2557_PAGE_REG(nRegister), nMask, nValue); + if (nResult < 0) + goto end; + } + + if (chn & channel_right) + nResult = tas2557_i2c_update_bits(pTAS2557, + pTAS2557->mnRAddr, TAS2557_PAGE_REG(nRegister), nMask, nValue); + +end: + mutex_unlock(&pTAS2557->dev_lock); + return nResult; +} + +void tas2557_clearIRQ(struct tas2557_priv *pTAS2557) +{ + unsigned int nValue; + int nResult = 0; + + nResult = pTAS2557->read(pTAS2557, channel_left, TAS2557_FLAGS_1, &nValue); + if (nResult >= 0) + pTAS2557->read(pTAS2557, channel_left, TAS2557_FLAGS_2, &nValue); + + nResult = pTAS2557->read(pTAS2557, channel_right, TAS2557_FLAGS_1, &nValue); + if (nResult >= 0) + pTAS2557->read(pTAS2557, channel_right, TAS2557_FLAGS_2, &nValue); +} + +void tas2557_enableIRQ(struct tas2557_priv *pTAS2557, enum channel chl, bool enable) +{ + static bool bLeftChlEnable; + static bool bRightChlEnable; + + if (enable) { + if (!pTAS2557->mbIRQEnable) { + if (chl & channel_left) { + if (gpio_is_valid(pTAS2557->mnLeftChlGpioINT)) { + enable_irq(pTAS2557->mnLeftChlIRQ); + bLeftChlEnable = true; + } else + bLeftChlEnable = false; + } + + if (chl & channel_right) { + if (gpio_is_valid(pTAS2557->mnRightChlGpioINT)) { + if (pTAS2557->mnRightChlIRQ != pTAS2557->mnLeftChlIRQ) { + enable_irq(pTAS2557->mnRightChlIRQ); + bRightChlEnable = true; + } else if (!bLeftChlEnable) { + enable_irq(pTAS2557->mnRightChlIRQ); + bRightChlEnable = true; + } else + bRightChlEnable = false; + } else + bRightChlEnable = false; + } + if (bLeftChlEnable || bRightChlEnable) { + /* check after 10 ms */ + schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(10)); + } + pTAS2557->mbIRQEnable = true; + } + } else { + if (pTAS2557->mbIRQEnable) { + if (gpio_is_valid(pTAS2557->mnLeftChlGpioINT)) { + if (bLeftChlEnable) { + disable_irq_nosync(pTAS2557->mnLeftChlIRQ); + bLeftChlEnable = false; + } + } + if (gpio_is_valid(pTAS2557->mnRightChlGpioINT)) { + if (bRightChlEnable) { + disable_irq_nosync(pTAS2557->mnRightChlIRQ); + bRightChlEnable = false; + } + } + pTAS2557->mbIRQEnable = false; + } + } +} + +static void tas2557_hw_reset(struct tas2557_priv *pTAS2557) +{ + dev_dbg(pTAS2557->dev, "%s\n", __func__); + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioRst)) { + gpio_direction_output(pTAS2557->mnLeftChlGpioRst, 0); + msleep(5); + gpio_direction_output(pTAS2557->mnLeftChlGpioRst, 1); + msleep(2); + } + + if (gpio_is_valid(pTAS2557->mnRightChlGpioRst) + && (pTAS2557->mnLeftChlGpioRst != pTAS2557->mnRightChlGpioRst)) { + gpio_direction_output(pTAS2557->mnRightChlGpioRst, 0); + msleep(5); + gpio_direction_output(pTAS2557->mnRightChlGpioRst, 1); + msleep(2); + } + + pTAS2557->mnLCurrentBook = -1; + pTAS2557->mnLCurrentPage = -1; + pTAS2557->mnRCurrentBook = -1; + pTAS2557->mnRCurrentPage = -1; + if (pTAS2557->mnErrCode) + dev_info(pTAS2557->dev, "before reset, ErrCode=0x%x\n", pTAS2557->mnErrCode); + pTAS2557->mnErrCode = 0; +} + +static void irq_work_routine(struct work_struct *work) +{ + struct tas2557_priv *pTAS2557 = + container_of(work, struct tas2557_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_TAS2557_CODEC_STEREO + mutex_lock(&pTAS2557->codec_lock); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_lock(&pTAS2557->file_lock); +#endif + if (pTAS2557->mnErrCode & ERROR_FAILSAFE) + goto program; + + if (pTAS2557->mbRuntimeSuspend) { + dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + + if (!pTAS2557->mbPowerUp) { + dev_info(pTAS2557->dev, "%s, device not powered\n", __func__); + goto end; + } + + if ((!pTAS2557->mpFirmware->mnConfigurations) + || (!pTAS2557->mpFirmware->mnPrograms)) { + dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]); + if (pConfiguration->mnDevices & channel_left) { + nResult = tas2557_dev_read(pTAS2557, channel_left, TAS2557_FLAGS_1, &nDevLInt1Status); + if (nResult >= 0) + nResult = tas2557_dev_read(pTAS2557, channel_left, TAS2557_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(pTAS2557->dev, "IRQ critical Error L: 0x%x, 0x%x\n", + nDevLInt1Status, nDevLInt2Status); + if (nDevLInt1Status & 0x80) { + pTAS2557->mnErrCode |= ERROR_OVER_CURRENT; + dev_err(pTAS2557->dev, "DEVA SPK over current!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_OVER_CURRENT; + + if (nDevLInt1Status & 0x40) { + pTAS2557->mnErrCode |= ERROR_UNDER_VOLTAGE; + dev_err(pTAS2557->dev, "DEVA SPK under voltage!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_UNDER_VOLTAGE; + + if (nDevLInt1Status & 0x20) { + pTAS2557->mnErrCode |= ERROR_CLK_HALT; + dev_err(pTAS2557->dev, "DEVA clk halted!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_HALT; + + if (nDevLInt1Status & 0x10) { + pTAS2557->mnErrCode |= ERROR_DIE_OVERTEMP; + dev_err(pTAS2557->dev, "DEVA die over temperature!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_DIE_OVERTEMP; + + if (nDevLInt1Status & 0x08) { + pTAS2557->mnErrCode |= ERROR_BROWNOUT; + dev_err(pTAS2557->dev, "DEVA brownout!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_BROWNOUT; + + if (nDevLInt1Status & 0x04) { + pTAS2557->mnErrCode |= ERROR_CLK_LOST; + dev_err(pTAS2557->dev, "DEVA clock lost!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_LOST; + + if (nDevLInt2Status & 0x08) { + pTAS2557->mnErrCode |= ERROR_CLK_DET1; + dev_err(pTAS2557->dev, "DEVA clk detection 1!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_DET1; + + if (nDevLInt2Status & 0x04) { + pTAS2557->mnErrCode |= ERROR_CLK_DET2; + dev_err(pTAS2557->dev, "DEVA clk detection 2!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_DET2; + + goto program; + } else { + nCounter = 2; + while (nCounter > 0) { + nResult = tas2557_dev_read(pTAS2557, channel_left, TAS2557_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 TAS2557 */ + dev_dbg(pTAS2557->dev, "PowSts L: 0x%x, check again after 10ms\n", + nDevLInt1Status); + msleep(10); + } + } + if ((nDevLInt1Status & 0xc0) != 0xc0) { + dev_err(pTAS2557->dev, "%s, Critical DevA ERROR B[%d]_P[%d]_R[%d]= 0x%x\n", + __func__, + TAS2557_BOOK_ID(TAS2557_POWER_UP_FLAG_REG), + TAS2557_PAGE_ID(TAS2557_POWER_UP_FLAG_REG), + TAS2557_PAGE_REG(TAS2557_POWER_UP_FLAG_REG), + nDevLInt1Status); + pTAS2557->mnErrCode |= ERROR_CLASSD_PWR; + goto program; + } + pTAS2557->mnErrCode &= ~ERROR_CLASSD_PWR; + } + } + + if (pConfiguration->mnDevices & channel_right) { + nResult = tas2557_dev_read(pTAS2557, channel_right, TAS2557_FLAGS_1, &nDevRInt1Status); + if (nResult >= 0) + nResult = tas2557_dev_read(pTAS2557, channel_right, TAS2557_FLAGS_2, &nDevRInt2Status); + else + goto program; + + if (((nDevRInt1Status & 0xfc) != 0) || ((nDevRInt2Status & 0x0c) != 0)) { + /* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */ + dev_dbg(pTAS2557->dev, "IRQ critical Error R: 0x%x, 0x%x\n", + nDevRInt1Status, nDevRInt2Status); + if (nDevRInt1Status & 0x80) { + pTAS2557->mnErrCode |= ERROR_OVER_CURRENT; + dev_err(pTAS2557->dev, "DEVB SPK over current!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_OVER_CURRENT; + + if (nDevRInt1Status & 0x40) { + pTAS2557->mnErrCode |= ERROR_UNDER_VOLTAGE; + dev_err(pTAS2557->dev, "DEVB SPK under voltage!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_UNDER_VOLTAGE; + + if (nDevRInt1Status & 0x20) { + pTAS2557->mnErrCode |= ERROR_CLK_HALT; + dev_err(pTAS2557->dev, "DEVB clk halted!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_HALT; + + if (nDevRInt1Status & 0x10) { + pTAS2557->mnErrCode |= ERROR_DIE_OVERTEMP; + dev_err(pTAS2557->dev, "DEVB die over temperature!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_DIE_OVERTEMP; + + if (nDevRInt1Status & 0x08) { + pTAS2557->mnErrCode |= ERROR_BROWNOUT; + dev_err(pTAS2557->dev, "DEVB brownout!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_BROWNOUT; + + if (nDevRInt1Status & 0x04) { + pTAS2557->mnErrCode |= ERROR_CLK_LOST; + dev_err(pTAS2557->dev, "DEVB clock lost!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_LOST; + + if (nDevRInt2Status & 0x08) { + pTAS2557->mnErrCode |= ERROR_CLK_DET1; + dev_err(pTAS2557->dev, "DEVB clk detection 1!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_DET1; + + if (nDevRInt2Status & 0x04) { + pTAS2557->mnErrCode |= ERROR_CLK_DET2; + dev_err(pTAS2557->dev, "DEVB clk detection 2!\n"); + } else + pTAS2557->mnErrCode &= ~ERROR_CLK_DET2; + goto program; + } else { + nCounter = 2; + while (nCounter > 0) { + nResult = tas2557_dev_read(pTAS2557, channel_right, TAS2557_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 TAS2557 */ + dev_dbg(pTAS2557->dev, "PowSts R: 0x%x, check again after 10ms\n", + nDevRInt1Status); + msleep(10); + } + } + if ((nDevRInt1Status & 0xc0) != 0xc0) { + dev_err(pTAS2557->dev, "%s, Critical DevB ERROR B[%d]_P[%d]_R[%d]= 0x%x\n", + __func__, + TAS2557_BOOK_ID(TAS2557_POWER_UP_FLAG_REG), + TAS2557_PAGE_ID(TAS2557_POWER_UP_FLAG_REG), + TAS2557_PAGE_REG(TAS2557_POWER_UP_FLAG_REG), + nDevRInt1Status); + pTAS2557->mnErrCode |= ERROR_CLASSD_PWR; + goto program; + } + pTAS2557->mnErrCode &= ~ERROR_CLASSD_PWR; + } + } + + goto end; + +program: + /* hardware reset and reload */ + tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration); + +end: + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_unlock(&pTAS2557->file_lock); +#endif + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_unlock(&pTAS2557->codec_lock); +#endif +} + +static irqreturn_t tas2557_irq_handler(int irq, void *dev_id) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)dev_id; + + tas2557_enableIRQ(pTAS2557, channel_both, false); + /* get IRQ status after 100 ms */ + schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +static enum hrtimer_restart temperature_timer_func(struct hrtimer *timer) +{ + struct tas2557_priv *pTAS2557 = container_of(timer, struct tas2557_priv, mtimer); + + if (pTAS2557->mbPowerUp) { + schedule_work(&pTAS2557->mtimerwork); + schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(1)); + } + return HRTIMER_NORESTART; +} + +static void timer_work_routine(struct work_struct *work) +{ + struct tas2557_priv *pTAS2557 = container_of(work, struct tas2557_priv, mtimerwork); + int nResult, nTemp, nActTemp; + struct TProgram *pProgram; + static int nAvg; + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_lock(&pTAS2557->codec_lock); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_lock(&pTAS2557->file_lock); +#endif + + if (pTAS2557->mbRuntimeSuspend) { + dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + + if (!pTAS2557->mpFirmware->mnConfigurations) { + dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + if (!pTAS2557->mbPowerUp + || (pProgram->mnAppMode != TAS2557_APP_TUNINGMODE)) { + dev_info(pTAS2557->dev, "%s, pass, Pow=%d, program=%s\n", + __func__, pTAS2557->mbPowerUp, pProgram->mpName); + goto end; + } + + nResult = tas2557_get_die_temperature(pTAS2557, &nTemp); + if (nResult >= 0) { + nActTemp = (int)(nTemp >> 23); + dev_dbg(pTAS2557->dev, "Die=0x%x, degree=%d\n", nTemp, nActTemp); + if (!pTAS2557->mnDieTvReadCounter) + nAvg = 0; + pTAS2557->mnDieTvReadCounter++; + nAvg += nActTemp; + if (!(pTAS2557->mnDieTvReadCounter % LOW_TEMPERATURE_COUNTER)) { + nAvg /= LOW_TEMPERATURE_COUNTER; + dev_dbg(pTAS2557->dev, "check : avg=%d\n", nAvg); + if ((nAvg & 0x80000000) != 0) { + /* if Die temperature is below ZERO */ + if (pTAS2557->mnDevCurrentGain != LOW_TEMPERATURE_GAIN) { + nResult = tas2557_set_DAC_gain(pTAS2557, channel_both, LOW_TEMPERATURE_GAIN); + if (nResult < 0) + goto end; + pTAS2557->mnDevCurrentGain = LOW_TEMPERATURE_GAIN; + dev_dbg(pTAS2557->dev, "LOW Temp: set gain to %d\n", LOW_TEMPERATURE_GAIN); + } + } else if (nAvg > 5) { + /* if Die temperature is above 5 degree C */ + if (pTAS2557->mnDevCurrentGain != pTAS2557->mnDevGain) { + nResult = tas2557_set_DAC_gain(pTAS2557, channel_both, pTAS2557->mnDevGain); + if (nResult < 0) + goto end; + pTAS2557->mnDevCurrentGain = pTAS2557->mnDevGain; + dev_dbg(pTAS2557->dev, "LOW Temp: set gain to original\n"); + } + } + nAvg = 0; + } + + if (pTAS2557->mbPowerUp) + hrtimer_start(&pTAS2557->mtimer, + ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + +end: + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_unlock(&pTAS2557->file_lock); +#endif + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_unlock(&pTAS2557->codec_lock); +#endif +} + +static int tas2557_runtime_suspend(struct tas2557_priv *pTAS2557) +{ + dev_dbg(pTAS2557->dev, "%s\n", __func__); + + pTAS2557->mbRuntimeSuspend = true; + + if (hrtimer_active(&pTAS2557->mtimer)) { + dev_dbg(pTAS2557->dev, "cancel die temp timer\n"); + hrtimer_cancel(&pTAS2557->mtimer); + } + if (work_pending(&pTAS2557->mtimerwork)) { + dev_dbg(pTAS2557->dev, "cancel timer work\n"); + cancel_work_sync(&pTAS2557->mtimerwork); + } + if (delayed_work_pending(&pTAS2557->irq_work)) { + dev_dbg(pTAS2557->dev, "cancel IRQ work\n"); + cancel_delayed_work_sync(&pTAS2557->irq_work); + } + + return 0; +} + +static int tas2557_runtime_resume(struct tas2557_priv *pTAS2557) +{ + struct TProgram *pProgram; + + dev_dbg(pTAS2557->dev, "%s\n", __func__); + if (!pTAS2557->mpFirmware->mpPrograms) { + dev_dbg(pTAS2557->dev, "%s, firmware not loaded\n", __func__); + goto end; + } + + if (pTAS2557->mnCurrentProgram >= pTAS2557->mpFirmware->mnPrograms) { + dev_err(pTAS2557->dev, "%s, firmware corrupted\n", __func__); + goto end; + } + + pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); + if (pTAS2557->mbPowerUp && (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE)) { + if (!hrtimer_active(&pTAS2557->mtimer)) { + dev_dbg(pTAS2557->dev, "%s, start Die Temp check timer\n", __func__); + pTAS2557->mnDieTvReadCounter = 0; + hrtimer_start(&pTAS2557->mtimer, + ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + } + + pTAS2557->mbRuntimeSuspend = false; +end: + + return 0; +} + +static bool tas2557_volatile(struct device *pDev, unsigned int nRegister) +{ + return true; +} + +static bool tas2557_writeable(struct device *pDev, unsigned int nRegister) +{ + return true; +} + +static const struct regmap_config tas2557_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2557_writeable, + .volatile_reg = tas2557_volatile, + .cache_type = REGCACHE_NONE, + .max_register = 128, +}; + +/* tas2557_i2c_probe : + * platform dependent + * should implement hardware reset functionality + */ +atomic_t tas2557_ref_count; +static int tas2557_i2c_probe(struct i2c_client *pClient) +{ + struct tas2557_priv *pTAS2557; + int nResult = 0; + unsigned int nValue = 0; + + dev_info(&pClient->dev, "%s enter\n", __func__); + + pTAS2557 = devm_kzalloc(&pClient->dev, sizeof(struct tas2557_priv), GFP_KERNEL); + if (!pTAS2557) { + nResult = -ENOMEM; + goto err; + } + + pTAS2557->client = pClient; + pTAS2557->dev = &pClient->dev; + i2c_set_clientdata(pClient, pTAS2557); + dev_set_drvdata(&pClient->dev, pTAS2557); + + pTAS2557->mpRegmap = devm_regmap_init_i2c(pClient, &tas2557_i2c_regmap); + if (IS_ERR(pTAS2557->mpRegmap)) { + nResult = PTR_ERR(pTAS2557->mpRegmap); + dev_err(&pClient->dev, "Failed to allocate register map: %d\n", + nResult); + goto err; + } + + if (pClient->dev.of_node) + tas2557_parse_dt(&pClient->dev, pTAS2557); + + atomic_set(&tas2557_ref_count, 0); + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioRst)) { + nResult = gpio_request(pTAS2557->mnLeftChlGpioRst, "TAS2557-RESET-Left"); + if (nResult < 0) { + dev_err(pTAS2557->dev, "%s: GPIO %d request error\n", + __func__, pTAS2557->mnLeftChlGpioRst); + goto err; + } + } + + if (gpio_is_valid(pTAS2557->mnRightChlGpioRst) + && (pTAS2557->mnLeftChlGpioRst != pTAS2557->mnRightChlGpioRst)) { + nResult = gpio_request(pTAS2557->mnRightChlGpioRst, "TAS2557-RESET-Right"); + if (nResult < 0) { + dev_err(pTAS2557->dev, "%s: GPIO %d request error\n", + __func__, pTAS2557->mnRightChlGpioRst); + goto err; + } + } + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioRst) + || gpio_is_valid(pTAS2557->mnRightChlGpioRst)) + tas2557_hw_reset(pTAS2557); + + pTAS2557->read = tas2557_dev_read; + pTAS2557->write = tas2557_dev_write; + pTAS2557->bulk_read = tas2557_dev_bulk_read; + pTAS2557->bulk_write = tas2557_dev_bulk_write; + pTAS2557->update_bits = tas2557_dev_update_bits; + pTAS2557->enableIRQ = tas2557_enableIRQ; + pTAS2557->clearIRQ = tas2557_clearIRQ; + pTAS2557->set_config = tas2557_set_config; + pTAS2557->set_calibration = tas2557_set_calibration; + pTAS2557->hw_reset = tas2557_hw_reset; + pTAS2557->runtime_suspend = tas2557_runtime_suspend; + pTAS2557->runtime_resume = tas2557_runtime_resume; + pTAS2557->mnRestart = 0; + + mutex_init(&pTAS2557->dev_lock); + + /* Reset the chip */ + nResult = tas2557_dev_write(pTAS2557, channel_both, TAS2557_SW_RESET_REG, 1); + if (nResult < 0) { + dev_err(&pClient->dev, "I2c fail, %d\n", nResult); + goto err; + } + + msleep(1); + tas2557_dev_read(pTAS2557, channel_left, TAS2557_REV_PGID_REG, &nValue); + pTAS2557->mnLPGID = nValue; + tas2557_dev_read(pTAS2557, channel_right, TAS2557_REV_PGID_REG, &nValue); + pTAS2557->mnRPGID = nValue; + if (pTAS2557->mnLPGID != pTAS2557->mnRPGID) { + dev_err(pTAS2557->dev, "HardWare Critical: L-PGID=0x%x, R-PGID=0x%x, please use same version\n", + pTAS2557->mnLPGID, pTAS2557->mnRPGID); + nResult = -ENOTSUPP; + goto err; + } + + if (pTAS2557->mnLPGID == TAS2557_PG_VERSION_2P1) + dev_info(pTAS2557->dev, "PG2.1 found\n"); + else if (pTAS2557->mnLPGID == TAS2557_PG_VERSION_1P0) + dev_info(pTAS2557->dev, "PG1.0 found\n"); + else { + dev_err(pTAS2557->dev, "PGID = 0x%x, not support\n", pTAS2557->mnLPGID); + nResult = -ENOTSUPP; + goto err; + } + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioINT)) { + nResult = gpio_request(pTAS2557->mnLeftChlGpioINT, "TAS2557-LeftCHL-IRQ"); + if (nResult < 0) { + dev_err(pTAS2557->dev, + "%s: GPIO %d request INT error\n", + __func__, pTAS2557->mnLeftChlGpioINT); + goto err; + } + + gpio_direction_input(pTAS2557->mnLeftChlGpioINT); + pTAS2557->mnLeftChlIRQ = gpio_to_irq(pTAS2557->mnLeftChlGpioINT); + dev_dbg(pTAS2557->dev, "irq = %d\n", pTAS2557->mnLeftChlIRQ); + nResult = request_threaded_irq(pTAS2557->mnLeftChlIRQ, tas2557_irq_handler, + NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + pClient->name, pTAS2557); + if (nResult < 0) { + dev_err(pTAS2557->dev, + "request_irq failed, %d\n", nResult); + goto err; + } + disable_irq_nosync(pTAS2557->mnLeftChlIRQ); + } + + if (gpio_is_valid(pTAS2557->mnRightChlGpioINT)) { + if (pTAS2557->mnLeftChlGpioINT != pTAS2557->mnRightChlGpioINT) { + nResult = gpio_request(pTAS2557->mnRightChlGpioINT, "TAS2557-RightCHL-IRQ"); + if (nResult < 0) { + dev_err(pTAS2557->dev, + "%s: GPIO %d request INT error\n", + __func__, pTAS2557->mnRightChlGpioINT); + goto err; + } + + gpio_direction_input(pTAS2557->mnRightChlGpioINT); + pTAS2557->mnRightChlIRQ = gpio_to_irq(pTAS2557->mnRightChlGpioINT); + dev_dbg(pTAS2557->dev, "irq = %d\n", pTAS2557->mnRightChlIRQ); + nResult = request_threaded_irq(pTAS2557->mnRightChlIRQ, tas2557_irq_handler, + NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + pClient->name, pTAS2557); + if (nResult < 0) { + dev_err(pTAS2557->dev, + "request_irq failed, %d\n", nResult); + goto err; + } + disable_irq_nosync(pTAS2557->mnRightChlIRQ); + } else + pTAS2557->mnRightChlIRQ = pTAS2557->mnLeftChlIRQ; + } + + if (gpio_is_valid(pTAS2557->mnLeftChlGpioINT) + || gpio_is_valid(pTAS2557->mnRightChlGpioINT)) { + INIT_DELAYED_WORK(&pTAS2557->irq_work, irq_work_routine); + } + + pTAS2557->mpFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL); + if (!pTAS2557->mpFirmware) { + nResult = -ENOMEM; + goto err; + } + + pTAS2557->mpCalFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL); + if (!pTAS2557->mpCalFirmware) { + nResult = -ENOMEM; + goto err; + } + +#ifdef CONFIG_TAS2557_CODEC_STEREO + mutex_init(&pTAS2557->codec_lock); + tas2557_register_codec(pTAS2557); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + mutex_init(&pTAS2557->file_lock); + tas2557_register_misc(pTAS2557); +#endif + +#ifdef ENABLE_TILOAD + tiload_driver_init(pTAS2557); +#endif + + hrtimer_init(&pTAS2557->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pTAS2557->mtimer.function = temperature_timer_func; + INIT_WORK(&pTAS2557->mtimerwork, timer_work_routine); + + nResult = request_firmware_nowait(THIS_MODULE, 1, TAS2557_FW_NAME, + pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready); + +err: + + return nResult; +} + +static void tas2557_i2c_remove(struct i2c_client *pClient) +{ + struct tas2557_priv *pTAS2557 = i2c_get_clientdata(pClient); + + dev_info(pTAS2557->dev, "%s\n", __func__); + +#ifdef CONFIG_TAS2557_CODEC_STEREO + tas2557_deregister_codec(pTAS2557); + mutex_destroy(&pTAS2557->codec_lock); +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + tas2557_deregister_misc(pTAS2557); + mutex_destroy(&pTAS2557->file_lock); +#endif + + mutex_destroy(&pTAS2557->dev_lock); +} + +static const struct i2c_device_id tas2557_i2c_id[] = { + {"tas2557s", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tas2557_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2557_of_match[] = { + {.compatible = "ti,tas2557s"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, tas2557_of_match); +#endif + +static struct i2c_driver tas2557_i2c_driver = { + .driver = { + .name = "tas2557s", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(tas2557_of_match), +#endif + }, + .probe = tas2557_i2c_probe, + .remove = tas2557_i2c_remove, + .id_table = tas2557_i2c_id, +}; + +module_i2c_driver(tas2557_i2c_driver); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2557 Stereo I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); + +#endif diff --git a/sound/soc/codecs/tas2557_stereo/tas2557.h b/sound/soc/codecs/tas2557_stereo/tas2557.h new file mode 100644 index 00000000000000..127462f1cf01a9 --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tas2557.h @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tas2557.h + * Description: definitions and data structures for TAS2557 Android Linux driver + */ + +#ifndef _TAS2557_H +#define _TAS2557_H + +#include +#include +#include + +/* Page Control Register */ +#define TAS2557_PAGECTL_REG 0 + +/* Book Control Register (available in page0 of each book) */ +#define TAS2557_BOOKCTL_PAGE 0 +#define TAS2557_BOOKCTL_REG 127 + +/* 0000 0000 0BBB BBBB BPPP PPPP PRRR RRRR */ +#define TAS2557_REG(book, page, reg) ((((unsigned int)book * 256 * 128) + \ + ((unsigned int)page * 128)) + reg) + +#define TAS2557_BOOK_ID(reg) ((unsigned char)(reg / (256 * 128))) +#define TAS2557_PAGE_ID(reg) ((unsigned char)((reg % (256 * 128)) / 128)) +#define TAS2557_BOOK_REG(reg) ((unsigned char)(reg % (256 * 128))) +#define TAS2557_PAGE_REG(reg) ((unsigned char)((reg % (256 * 128)) % 128)) + +/* Book0, Page0 registers */ +#define TAS2557_SW_RESET_REG TAS2557_REG(0, 0, 1) + +#define TAS2557_REV_PGID_REG TAS2557_REG(0, 0, 3) +#define TAS2557_PG_VERSION_1P0 0x80 +#define TAS2557_PG_VERSION_2P0 0x90 +#define TAS2557_PG_VERSION_2P1 0xa0 + +#define TAS2557_POWER_CTRL1_REG TAS2557_REG(0, 0, 4) +#define TAS2557_POWER_CTRL2_REG TAS2557_REG(0, 0, 5) + +#define TAS2557_SPK_CTRL_REG TAS2557_REG(0, 0, 6) +/* B0P0R6 - TAS2557_SPK_CTRL_REG */ +#define TAS2557_DAC_GAIN_MASK (0xf << 3) +#define TAS2557_DAC_GAIN_SHIFT 0x03 + +#define TAS2557_MUTE_REG TAS2557_REG(0, 0, 7) +#define TAS2557_SNS_CTRL_REG TAS2557_REG(0, 0, 8) +#define TAS2557_ADC_INPUT_SEL_REG TAS2557_REG(0, 0, 9) +#define TAS2557_DBOOST_CTL_REG TAS2557_REG(0, 0, 10) +#define TAS2557_NONAME11_REG TAS2557_REG(0, 0, 11) +#define TAS2557_NONAME12_REG TAS2557_REG(0, 0, 12) +#define TAS2557_NONAME13_REG TAS2557_REG(0, 0, 13) +#define TAS2557_NONAME14_REG TAS2557_REG(0, 0, 14) +#define TAS2557_NONAME15_REG TAS2557_REG(0, 0, 15) +#define TAS2557_NONAME16_REG TAS2557_REG(0, 0, 16) +#define TAS2557_NONAME17_REG TAS2557_REG(0, 0, 17) +#define TAS2557_NONAME18_REG TAS2557_REG(0, 0, 18) +#define TAS2557_SAR_SAMPLING_TIME_REG TAS2557_REG(0, 0, 19) +#define TAS2557_SAR_ADC1_REG TAS2557_REG(0, 0, 20) +#define TAS2557_SAR_ADC2_REG TAS2557_REG(0, 0, 21) /* B0_P0_R0x15*/ +#define TAS2557_CRC_CHECKSUM_REG TAS2557_REG(0, 0, 32) +#define TAS2557_CRC_RESET_REG TAS2557_REG(0, 0, 33) +#define TAS2557_DSP_MODE_SELECT_REG TAS2557_REG(0, 0, 34) +#define TAS2557_SAFE_GUARD_REG TAS2557_REG(0, 0, 37) +#define TAS2557_ASI_CTL1_REG TAS2557_REG(0, 0, 42) +#define TAS2557_CLK_ERR_CTRL TAS2557_REG(0, 0, 44) /* B0_P0_R0x2c*/ +#define TAS2557_CLK_ERR_CTRL2 TAS2557_REG(0, 0, 45) /* B0_P0_R0x2d*/ +#define TAS2557_CLK_ERR_CTRL3 TAS2557_REG(0, 0, 46) /* B0_P0_R0x2e*/ +#define TAS2557_DBOOST_CFG_REG TAS2557_REG(0, 0, 52) +#define TAS2557_POWER_UP_FLAG_REG TAS2557_REG(0, 0, 100) +#define TAS2557_FLAGS_1 TAS2557_REG(0, 0, 104) /* B0_P0_R0x68*/ +#define TAS2557_FLAGS_2 TAS2557_REG(0, 0, 108) /* B0_P0_R0x6c*/ + +/* Book0, Page1 registers */ +#define TAS2557_ASI1_DAC_FORMAT_REG TAS2557_REG(0, 1, 1) +#define TAS2557_ASI1_ADC_FORMAT_REG TAS2557_REG(0, 1, 2) +#define TAS2557_ASI1_OFFSET1_REG TAS2557_REG(0, 1, 3) +#define TAS2557_ASI1_ADC_PATH_REG TAS2557_REG(0, 1, 7) +#define TAS2557_ASI1_DAC_BCLK_REG TAS2557_REG(0, 1, 8) +#define TAS2557_ASI1_DAC_WCLK_REG TAS2557_REG(0, 1, 9) +#define TAS2557_ASI1_ADC_BCLK_REG TAS2557_REG(0, 1, 10) +#define TAS2557_ASI1_ADC_WCLK_REG TAS2557_REG(0, 1, 11) +#define TAS2557_ASI1_DIN_DOUT_MUX_REG TAS2557_REG(0, 1, 12) +#define TAS2557_ASI1_BDIV_CLK_SEL_REG TAS2557_REG(0, 1, 13) +#define TAS2557_ASI1_BDIV_CLK_RATIO_REG TAS2557_REG(0, 1, 14) +#define TAS2557_ASI1_WDIV_CLK_RATIO_REG TAS2557_REG(0, 1, 15) +#define TAS2557_ASI1_DAC_CLKOUT_REG TAS2557_REG(0, 1, 16) +#define TAS2557_ASI1_ADC_CLKOUT_REG TAS2557_REG(0, 1, 17) +#define TAS2557_ASI2_DAC_FORMAT_REG TAS2557_REG(0, 1, 21) +#define TAS2557_ASI2_ADC_FORMAT_REG TAS2557_REG(0, 1, 22) +#define TAS2557_ASI2_OFFSET1_REG TAS2557_REG(0, 1, 23) +#define TAS2557_ASI2_ADC_PATH_REG TAS2557_REG(0, 1, 27) +#define TAS2557_ASI2_DAC_BCLK_REG TAS2557_REG(0, 1, 28) +#define TAS2557_ASI2_DAC_WCLK_REG TAS2557_REG(0, 1, 29) +#define TAS2557_ASI2_ADC_BCLK_REG TAS2557_REG(0, 1, 30) +#define TAS2557_ASI2_ADC_WCLK_REG TAS2557_REG(0, 1, 31) +#define TAS2557_ASI2_DIN_DOUT_MUX_REG TAS2557_REG(0, 1, 32) +#define TAS2557_ASI2_BDIV_CLK_SEL_REG TAS2557_REG(0, 1, 33) +#define TAS2557_ASI2_BDIV_CLK_RATIO_REG TAS2557_REG(0, 1, 34) +#define TAS2557_ASI2_WDIV_CLK_RATIO_REG TAS2557_REG(0, 1, 35) +#define TAS2557_ASI2_DAC_CLKOUT_REG TAS2557_REG(0, 1, 36) +#define TAS2557_ASI2_ADC_CLKOUT_REG TAS2557_REG(0, 1, 37) +#define TAS2557_GPIO1_PIN_REG TAS2557_REG(0, 1, 61) /*B0_P1_R0x3d */ +#define TAS2557_GPIO2_PIN_REG TAS2557_REG(0, 1, 62) /*B0_P1_R0x3e */ +#define TAS2557_GPIO3_PIN_REG TAS2557_REG(0, 1, 63) /*B0_P1_R0x3f */ +#define TAS2557_GPIO4_PIN_REG TAS2557_REG(0, 1, 64) /*B0_P1_R0x40 */ +#define TAS2557_GPIO5_PIN_REG TAS2557_REG(0, 1, 65) +#define TAS2557_GPIO6_PIN_REG TAS2557_REG(0, 1, 66) +#define TAS2557_GPIO7_PIN_REG TAS2557_REG(0, 1, 67) +#define TAS2557_GPIO8_PIN_REG TAS2557_REG(0, 1, 68) +#define TAS2557_GPIO9_PIN_REG TAS2557_REG(0, 1, 69) +#define TAS2557_GPIO10_PIN_REG TAS2557_REG(0, 1, 70) +#define TAS2557_GPI_PIN_REG TAS2557_REG(0, 1, 77) /*B0_P1_R0x4d */ +#define TAS2557_GPIO_HIZ_CTRL1_REG TAS2557_REG(0, 1, 79) +#define TAS2557_GPIO_HIZ_CTRL2_REG TAS2557_REG(0, 1, 80) /*B0_P1_R0x50 */ +#define TAS2557_GPIO_HIZ_CTRL3_REG TAS2557_REG(0, 1, 81) +#define TAS2557_GPIO_HIZ_CTRL4_REG TAS2557_REG(0, 1, 82) +#define TAS2557_GPIO_HIZ_CTRL5_REG TAS2557_REG(0, 1, 83) +#define TAS2557_BIT_BANG_CTRL_REG TAS2557_REG(0, 1, 87) +#define TAS2557_BIT_BANG_OUT1_REG TAS2557_REG(0, 1, 88) +#define TAS2557_BIT_BANG_OUT2_REG TAS2557_REG(0, 1, 89) +#define TAS2557_BIT_BANG_IN1_REG TAS2557_REG(0, 1, 90) +#define TAS2557_BIT_BANG_IN2_REG TAS2557_REG(0, 1, 91) +#define TAS2557_BIT_BANG_IN3_REG TAS2557_REG(0, 1, 92) +#define TAS2557_PDM_IN_CLK_REG TAS2557_REG(0, 1, 94) +#define TAS2557_PDM_IN_PIN_REG TAS2557_REG(0, 1, 95) +#define TAS2557_ASIM_IFACE1_REG TAS2557_REG(0, 1, 98) +#define TAS2557_ASIM_FORMAT_REG TAS2557_REG(0, 1, 99) +#define TAS2557_ASIM_IFACE3_REG TAS2557_REG(0, 1, 100) +#define TAS2557_ASIM_IFACE4_REG TAS2557_REG(0, 1, 101) +#define TAS2557_ASIM_IFACE5_REG TAS2557_REG(0, 1, 102) +#define TAS2557_ASIM_IFACE6_REG TAS2557_REG(0, 1, 103) +#define TAS2557_ASIM_IFACE7_REG TAS2557_REG(0, 1, 104) +#define TAS2557_ASIM_IFACE8_REG TAS2557_REG(0, 1, 105) +#define TAS2557_CLK_HALT_REG TAS2557_REG(0, 1, 106) /* B0_P1_R0x6a */ +#define TAS2557_INT_GEN1_REG TAS2557_REG(0, 1, 108) /* B0_P1_R0x6c */ +#define TAS2557_INT_GEN2_REG TAS2557_REG(0, 1, 109) /* B0_P1_R0x6d */ +#define TAS2557_INT_GEN3_REG TAS2557_REG(0, 1, 110) /* B0_P1_R0x6e */ +#define TAS2557_INT_GEN4_REG TAS2557_REG(0, 1, 111) /* B0_P1_R0x6f */ +#define TAS2557_INT_MODE_REG TAS2557_REG(0, 1, 114) /* B0_P1_R0x72 */ +#define TAS2557_MAIN_CLKIN_REG TAS2557_REG(0, 1, 115) +#define TAS2557_PLL_CLKIN_REG TAS2557_REG(0, 1, 116) +#define TAS2557_CLKOUT_MUX_REG TAS2557_REG(0, 1, 117) +#define TAS2557_CLKOUT_CDIV_REG TAS2557_REG(0, 1, 118) +#define TAS2557_HACK_GP01_REG TAS2557_REG(0, 1, 122) + +#define TAS2557_SLEEPMODE_CTL_REG TAS2557_REG(0, 2, 7) +#define TAS2557_HACK01_REG TAS2557_REG(0, 2, 10) + +#define TAS2557_ISENSE_THRESHOLD TAS2557_REG(0, 50, 104) +#define TAS2557_BOOSTON_EFFICIENCY TAS2557_REG(0, 51, 16) +#define TAS2557_BOOSTOFF_EFFICIENCY TAS2557_REG(0, 51, 20) +#define TAS2557_BOOST_HEADROOM TAS2557_REG(0, 51, 24) +#define TAS2557_THERMAL_FOLDBACK_REG TAS2557_REG(0, 51, 100) + +#define TAS2557_SA_PG2P1_CHL_CTRL_REG TAS2557_REG(0, 53, 20) /* B0_P0x35_R0x14 */ +#define TAS2557_SA_COEFF_SWAP_REG TAS2557_REG(0, 53, 44) /* B0_P0x35_R0x2c */ + +#define TAS2557_SA_PG1P0_CHL_CTRL_REG TAS2557_REG(0, 58, 120) /* B0_P0x3a_R0x78 */ + +#define TAS2557_TEST_MODE_REG TAS2557_REG(0, 253, 13) /* B0_P0xfd_R0x0d */ +#define TAS2557_BROADCAST_REG TAS2557_REG(0, 253, 54) /* B0_P0xfd_R0x36 */ +#define TAS2557_VBST_VOLT_REG TAS2557_REG(0, 253, 58) +#define TAS2557_CRYPTIC_REG TAS2557_REG(0, 253, 71) + +#define TAS2557_XMEM_687_REG TAS2557_REG(78, 23, 116) /* B0x78_P0x23_R0x40 */ + +#define TAS2557_PG2P1_CALI_R0_REG TAS2557_REG(0x8c, 0x2f, 0x40) +#define TAS2557_PG1P0_CALI_R0_REG TAS2557_REG(0x8c, 0x2f, 0x28) +#define TAS2557_PG2P1_CALI_T_REG TAS2557_REG(0x8c, 0x30, 0x20) +#define TAS2557_PG1P0_CALI_T_REG TAS2557_REG(0x8c, 0x30, 0x08) + +#define TAS2557_DAC_INTERPOL_REG TAS2557_REG(100, 0, 1) +#define TAS2557_SOFT_MUTE_REG TAS2557_REG(100, 0, 7) +#define TAS2557_PLL_P_VAL_REG TAS2557_REG(100, 0, 27) +#define TAS2557_PLL_J_VAL_REG TAS2557_REG(100, 0, 28) +#define TAS2557_PLL_D_VAL_MSB_REG TAS2557_REG(100, 0, 29) +#define TAS2557_PLL_D_VAL_LSB_REG TAS2557_REG(100, 0, 30) +#define TAS2557_CLK_MISC_REG TAS2557_REG(100, 0, 31) +#define TAS2557_PLL_N_VAL_REG TAS2557_REG(100, 0, 32) +#define TAS2557_DAC_MADC_VAL_REG TAS2557_REG(100, 0, 33) +#define TAS2557_ISENSE_DIV_REG TAS2557_REG(100, 0, 42) +#define TAS2557_RAMP_CLK_DIV_MSB_REG TAS2557_REG(100, 0, 43) +#define TAS2557_RAMP_CLK_DIV_LSB_REG TAS2557_REG(100, 0, 44) + +#define TAS2557_VBOOST_CTL_REG TAS2557_REG(100, 0, 64) + +#define TAS2557_DIE_TEMP_REG TAS2557_REG(130, 2, 124) /* B0x82_P0x02_R0x7C */ + +/* Bits */ +/* B0P0R4 - TAS2557_POWER_CTRL1_REG */ +#define TAS2557_SW_SHUTDOWN (0x1 << 0) +#define TAS2557_MADC_POWER_UP (0x1 << 3) +#define TAS2557_MDAC_POWER_UP (0x1 << 4) +#define TAS2557_NDIV_POWER_UP (0x1 << 5) +#define TAS2557_PLL_POWER_UP (0x1 << 6) +#define TAS2557_DSP_POWER_UP (0x1 << 7) + +/* B0P0R5 - TAS2557_POWER_CTRL2_REG */ +#define TAS2557_VSENSE_ENABLE (0x1 << 0) +#define TAS2557_ISENSE_ENABLE (0x1 << 1) +#define TAS2557_BOOST_ENABLE (0x1 << 5) +#define TAS2557_CLASSD_ENABLE (0x1 << 7) + +/* B0P0R7 - TAS2557_MUTE_REG */ +#define TAS2557_CLASSD_MUTE (0x1 << 0) +#define TAS2557_ISENSE_MUTE (0x1 << 1) + +/* B0P253R13 - TAS2557_TEST_MODE_REG */ +#define TAS2557_TEST_MODE_ENABLE (13) +#define TAS2557_TEST_MODE_MASK (0xf << 0) + +/* B0P253R71 - TAS2557_CRYPTIC_REG */ +#define TAS2557_OSC_TRIM_CAP(x) ((x & 0x3f) << 0) +#define TAS2557_DISABLE_ENCRYPTION (0x1 << 6) +#define TAS2557_SL_COMP (0x1 << 7) + +/* B0P1R115/6 - TAS2557_MAIN/PLL_CLKIN_REG */ +#define TAS2557_XXX_CLKIN_GPIO1 (0) +#define TAS2557_XXX_CLKIN_GPIO2 (1) +#define TAS2557_XXX_CLKIN_GPIO3 (2) +#define TAS2557_XXX_CLKIN_GPIO4 (3) +#define TAS2557_XXX_CLKIN_GPIO5 (4) +#define TAS2557_XXX_CLKIN_GPIO6 (5) +#define TAS2557_XXX_CLKIN_GPIO7 (6) +#define TAS2557_XXX_CLKIN_GPIO8 (7) +#define TAS2557_XXX_CLKIN_GPIO9 (8) +#define TAS2557_XXX_CLKIN_GPIO10 (9) +#define TAS2557_XXX_CLKIN_GPI1 (12) +#define TAS2557_XXX_CLKIN_GPI2 (13) +#define TAS2557_XXX_CLKIN_GPI3 (14) +#define TAS2557_NDIV_CLKIN_PLL (15) +#define TAS2557_PLL_CLKIN_INT_OSC (15) + +#define TAS2557_MCLK_CLKIN_SRC_GPIO1 (0) +#define TAS2557_MCLK_CLKIN_SRC_GPIO2 (1) +#define TAS2557_MCLK_CLKIN_SRC_GPIO3 (2) +#define TAS2557_MCLK_CLKIN_SRC_GPIO4 (3) +#define TAS2557_MCLK_CLKIN_SRC_GPIO5 (4) +#define TAS2557_MCLK_CLKIN_SRC_GPIO6 (5) +#define TAS2557_MCLK_CLKIN_SRC_GPIO7 (6) +#define TAS2557_MCLK_CLKIN_SRC_GPIO8 (7) +#define TAS2557_MCLK_CLKIN_SRC_GPIO9 (8) +#define TAS2557_MCLK_CLKIN_SRC_GPIO10 (9) +#define TAS2557_MCLK_CLKIN_SRC_GPI1 (12) +#define TAS2557_MCLK_CLKIN_SRC_GPI2 (13) +#define TAS2557_MCLK_CLKIN_SRC_GPI3 (14) + +#define TAS2557_FORMAT_I2S (0x0 << 5) +#define TAS2557_FORMAT_DSP (0x1 << 5) +#define TAS2557_FORMAT_RIGHT_J (0x2 << 5) +#define TAS2557_FORMAT_LEFT_J (0x3 << 5) +#define TAS2557_FORMAT_MONO_PCM (0x4 << 5) +#define TAS2557_FORMAT_MASK (0x7 << 5) + +#define TAS2557_WORDLENGTH_16BIT (0x0 << 3) +#define TAS2557_WORDLENGTH_20BIT (0x1 << 3) +#define TAS2557_WORDLENGTH_24BIT (0x2 << 3) +#define TAS2557_WORDLENGTH_32BIT (0x3 << 3) +#define TAS2557_WORDLENGTH_MASK TAS2557_WORDLENGTH_32BIT + +/* B100P0R7 - TAS2557_SOFT_MUTE_REG */ +#define TAS2557_PDM_SOFT_MUTE (0x1 << 0) +#define TAS2557_VSENSE_SOFT_MUTE (0x1 << 1) +#define TAS2557_ISENSE_SOFT_MUTE (0x1 << 2) +#define TAS2557_CLASSD_SOFT_MUTE (0x1 << 3) + +/* B100P0R27 - TAS2557_PLL_P_VAL_REG */ +#define TAS2557_PLL_P_VAL_MASK (0x3f << 0) + +/* B100P0R28 - TAS2557_PLL_J_VAL_REG */ +#define TAS2557_PLL_J_VAL_MASK ((unsigned int) (0x7f << 0)) +#define TAS2557_PLL_J_VAL_MASKX 0x00 + +/* B100P0R29-30 - TAS2557_PLL_D_VAL_MSB/LSB_REG */ +#define TAS2557_PLL_D_MSB_VAL(x) ((x >> 8) & 0x3f) +#define TAS2557_PLL_D_LSB_VAL(x) (x & 0xff) + +/* B100P0R31 - TAS2557_CLK_MISC_REG */ +#define TAS2557_DSP_CLK_FROM_PLL (0x1 << 5) + +#define TAS2557_FW_NAME "tas2557s_uCDSP.bin" + +#define TAS2557_BROADCAST_ADDR 0x4c + +#define TAS2557_APP_ROM1MODE 0 +#define TAS2557_APP_ROM2MODE 1 +#define TAS2557_APP_TUNINGMODE 2 +#define TAS2557_APP_ROM1_96KHZ 3 +#define TAS2557_APP_ROM2_96KHZ 4 +#define TAS2557_APP_RAMMODE 5 + +#define TAS2557_BOOST_OFF 0 +#define TAS2557_BOOST_DEVA 1 +#define TAS2557_BOOST_DEVB 2 +#define TAS2557_BOOST_BOTH 3 + +#define TAS2557_DM_AD_BD 0 /* DevA default, DevB default */ +#define TAS2557_DM_AM_BM 1 /* DevA mute, DevB mute */ +#define TAS2557_DM_AL_BR 2 /* DevA left channel, DevB right channel */ +#define TAS2557_DM_AR_BL 3 /* DevA right channel, DevB left channel */ +#define TAS2557_DM_AH_BH 4 /* DevA (L+R)/2, DevB (L+R)/2 */ + +#define TAS2557_VBST_DEFAULT 0 /* firmware default */ +#define TAS2557_VBST_A_ON 1 /* DevA always 8.5V, DevB default */ +#define TAS2557_VBST_B_ON 2 /* DevA default, DevB always 8.5V */ +#define TAS2557_VBST_A_ON_B_ON (TAS2557_VBST_A_ON | TAS2557_VBST_B_ON) /* both DevA and DevB always 8.5V */ +#define TAS2557_VBST_NEED_DEFAULT 0xff /* need default value */ + +#define TAS2557_VBST_8P5V 0 /* coresponding PPG 0dB */ +#define TAS2557_VBST_8P1V 1 /* coresponding PPG -1dB */ +#define TAS2557_VBST_7P6V 2 /* coresponding PPG -2dB */ +#define TAS2557_VBST_6P6V 3 /* coresponding PPG -3dB */ +#define TAS2557_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 FLAG_CHECK_COUNTER 25 + +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 tas2557_register { + int book; + int page; + int reg; +}; + +enum channel { + channel_left = 0x01, + channel_right = 0x02, + channel_both = (channel_left|channel_right), + channel_broadcast = 0x4, +}; + +enum echo_reference { + echoref_left = 0x00, + echoref_right = 0x01, + echoref_both = 0x02 +}; + +struct tas2557_priv { + struct device *dev; + struct regmap *mpRegmap; + struct i2c_client *client; + int mnLPGID; + int mnRPGID; + int mnLeftChlGpioRst; + int mnRightChlGpioRst; + struct mutex dev_lock; + struct TFirmware *mpFirmware; + struct TFirmware *mpCalFirmware; + unsigned int mnCurrentProgram; + unsigned int mnCurrentSampleRate; + unsigned int mnNewConfiguration; + unsigned int mnCurrentConfiguration; + unsigned int mnCurrentCalibration; + enum channel mnCurrentChannel; + unsigned char mnLAddr; + unsigned char mnRAddr; + unsigned char mnLCurrentBook; + unsigned char mnLCurrentPage; + unsigned char mnRCurrentBook; + unsigned char mnRCurrentPage; + unsigned int mnLBroadcastSet; + unsigned int mnRBroadcastSet; + bool mbTILoadActive; + bool mbPowerUp; + bool mbLoadConfigurationPrePowerUp; + bool mbLoadCalibrationPostPowerUp; + bool mbCalibrationLoaded; + int (*read)(struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int reg, + unsigned int *pValue); + int (*write)(struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int reg, + unsigned int Value); + int (*bulk_read)(struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int reg, + unsigned char *pData, + unsigned int len); + int (*bulk_write)(struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int reg, + unsigned char *pData, + unsigned int len); + int (*update_bits)(struct tas2557_priv *pTAS2557, + enum channel chn, + unsigned int reg, + unsigned int mask, + unsigned int value); + int (*set_config)(struct tas2557_priv *pTAS2557, + int config); + int (*set_calibration)(struct tas2557_priv *pTAS2557, + int calibration); + void (*clearIRQ)(struct tas2557_priv *pTAS2557); + void (*enableIRQ)(struct tas2557_priv *pTAS2557, + enum channel chl, bool enable); + void (*hw_reset)(struct tas2557_priv *pTAS2557); + /* device is working, but system is suspended */ + int (*runtime_suspend)(struct tas2557_priv *pTAS2557); + int (*runtime_resume)(struct tas2557_priv *pTAS2557); + + int mnLeftChlGpioINT; + int mnRightChlGpioINT; + struct delayed_work irq_work; + unsigned int mnLeftChlIRQ; + unsigned int mnRightChlIRQ; + bool mbIRQEnable; + enum echo_reference mnEchoRef; + unsigned char mnI2SBits; + + unsigned int mnChannelState; + unsigned char mnDevAChlData[16]; + unsigned char mnDevBChlData[16]; + + unsigned int mnVBoostState; + bool mbLoadVBoostPrePowerUp; + unsigned int mnVBoostVoltage; + unsigned int mnVBoostNewState; + unsigned int mnVBoostDefaultCfg[4]; + + /* for low temperature check */ + unsigned int mnDevGain; + unsigned int mnDevCurrentGain; + unsigned int mnDieTvReadCounter; + struct hrtimer mtimer; + struct work_struct mtimerwork; + + /* device is working, but system is suspended */ + bool mbRuntimeSuspend; + + unsigned int mnErrCode; + + /* for configurations with maximum TLimit 0x7fffffff, + * bypass calibration update, usually used in factory test + */ + bool mbBypassTMax; + + unsigned int mnRestart; + +#ifdef CONFIG_TAS2557_CODEC_STEREO + struct mutex codec_lock; +#endif + +#ifdef CONFIG_TAS2557_MISC_STEREO + int mnDBGCmd; + int mnCurrentReg; + struct mutex file_lock; +#endif + +}; + +#endif /* _TAS2557_H */ diff --git a/sound/soc/codecs/tas2557_stereo/tiload.c b/sound/soc/codecs/tas2557_stereo/tiload.c new file mode 100644 index 00000000000000..4dae467529e63e --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tiload.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tiload.c + * Description: utility for TAS2557 Android in-system tuning + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tiload.h" + +static struct cdev *tiload_cdev; +static int tiload_major; /* Dynamic allocation of Mjr No. */ +static int tiload_opened; /* Dynamic allocation of Mjr No. */ +static struct tas2557_priv *g_TAS2557; +struct class *tiload_class; +static unsigned int magic_num; + +static char gPage; +static char gBook; + +/* + * Function : tiload_open + * Purpose : open method for tiload programming interface + */ +static int tiload_open(struct inode *in, struct file *filp) +{ + struct tas2557_priv *pTAS2557 = g_TAS2557; + + dev_info(pTAS2557->dev, "%s\n", __func__); + + if (tiload_opened) { + dev_info(pTAS2557->dev, "%s device is already opened\n", "tiload"); + return -EINVAL; + } + filp->private_data = (void *)pTAS2557; + tiload_opened++; + return 0; +} + +/* + * Function : tiload_release + * Purpose : close method for tiload programming interface + */ +static int tiload_release(struct inode *in, struct file *filp) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data; + + dev_info(pTAS2557->dev, "%s\n", __func__); + filp->private_data = NULL; + tiload_opened--; + return 0; +} + +#define MAX_LENGTH 128 +/* + * Function : tiload_read + * Purpose : read from codec + */ +static ssize_t tiload_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data; + char *rd_data; + unsigned int nCompositeRegister = 0, Value = 0; + char reg_addr; + size_t size; + int ret = 0; + + dev_info(pTAS2557->dev, "%s\n", __func__); + if (count > MAX_LENGTH) { + dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH); + return -EINVAL; + } + + /* copy register address from user space */ + size = copy_from_user(®_addr, buf, 1); + if (size != 0) { + dev_err(pTAS2557->dev, "read: copy_from_user failure\n"); + return -EINVAL; + } + + size = count; + + rd_data = kmalloc(MAX_LENGTH + 1, GFP_KERNEL | GFP_DMA); + + if(rd_data == NULL) { + dev_err(pTAS2557->dev, "kmalloc fail \n"); + return -EINVAL; + } + + nCompositeRegister = BPR_REG(gBook, gPage, reg_addr); + if (count == 1) { + ret = pTAS2557->read(pTAS2557, pTAS2557->mnCurrentChannel, + 0x80000000 | nCompositeRegister, &Value); + if (ret >= 0) + rd_data[0] = (char) Value; + } else if (count > 1) { + ret = pTAS2557->bulk_read(pTAS2557, pTAS2557->mnCurrentChannel, + 0x80000000 | nCompositeRegister, rd_data, size); + } + if (ret < 0) + dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu error happen!\n", + __func__, __LINE__, ret, count); + +#ifdef DEBUG + dev_info(pTAS2557->dev, "read size = %d, reg_addr= %x , count = %d\n", + (int) size, reg_addr, (int) count); +#endif + if (size != count) + dev_err(pTAS2557->dev, "read %d registers from the codec\n", (int) size); + + if (copy_to_user(buf, rd_data, size) != 0) { + dev_err(pTAS2557->dev, "copy_to_user failed\n"); + kfree(rd_data); + return -EINVAL; + } + + kfree(rd_data); + return size; +} + +/* + * Function : tiload_write + * Purpose : write to codec + */ +static ssize_t tiload_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data; + char *wr_data; + char *pData; + size_t size; + unsigned int nCompositeRegister = 0; + unsigned int nRegister; + int ret = 0; + dev_info(pTAS2557->dev, "%s\n", __func__); + + if (count > MAX_LENGTH) { + dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH); + return -EINVAL; + } + + wr_data = kmalloc(MAX_LENGTH + 1, GFP_KERNEL | GFP_DMA); + if(wr_data == NULL) { + dev_err(pTAS2557->dev, "kmalloc fail \n"); + return -EINVAL; + } + pData = wr_data; + + /* copy buffer from user space */ + size = copy_from_user(wr_data, buf, count); + if (size != 0) { + dev_err(pTAS2557->dev, "copy_from_user failure %d\n", (int) size); + kfree(wr_data); + return -EINVAL; + } +#ifdef DEBUG + dev_info(pTAS2557->dev, "write size = %zu\n", count); +#endif + nRegister = wr_data[0]; + size = count; + if ((nRegister == 127) && (gPage == 0)) { + gBook = wr_data[1]; + kfree(wr_data); + return size; + } + + if (nRegister == 0) { + gPage = wr_data[1]; + pData++; + count--; + } + + nCompositeRegister = BPR_REG(gBook, gPage, nRegister); + if (count == 2) { + ret = pTAS2557->write(pTAS2557, pTAS2557->mnCurrentChannel, + 0x80000000 | nCompositeRegister, pData[1]); + } else if (count > 2) { + ret = pTAS2557->bulk_write(pTAS2557, pTAS2557->mnCurrentChannel, + 0x80000000 | nCompositeRegister, &pData[1], count - 1); + } + + if (ret < 0) + dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu, ERROR Happen\n", __func__, + __LINE__, ret, count); + kfree(wr_data); + return size; +} + +static void tiload_route_IO(struct tas2557_priv *pTAS2557, unsigned int bLock) +{ + if (bLock) + pTAS2557->write(pTAS2557, pTAS2557->mnCurrentChannel, 0xAFFEAFFE, 0xBABEBABE); + else + pTAS2557->write(pTAS2557, pTAS2557->mnCurrentChannel, 0xBABEBABE, 0xAFFEAFFE); +} + +static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data; + long num = 0; + void __user *argp = (void __user *) arg; + unsigned char addr = 0; + int val; + struct BPR bpr; + + switch (cmd) { + case TILOAD_IOMAGICNUM_GET: + dev_info(pTAS2557->dev, "TILOAD_IOMAGICNUM_GET=0x%x\n", cmd); + num = copy_to_user(argp, &magic_num, sizeof(int)); + break; + case TILOAD_IOMAGICNUM_SET: + dev_info(pTAS2557->dev, "TILOAD_IOMAGICNUM_SET=0x%x\n", cmd); + num = copy_from_user(&magic_num, argp, sizeof(int)); + tiload_route_IO(pTAS2557, magic_num); + break; + case TILOAD_BPR_READ: + dev_info(pTAS2557->dev, "TILOAD_BPR_READ=0x%x\n", cmd); + break; + case TILOAD_BPR_WRITE: + dev_info(pTAS2557->dev, "TILOAD_BPR_WRITE=0x%x\n", cmd); + num = copy_from_user(&bpr, argp, sizeof(struct BPR)); + break; + case TILOAD_IOCTL_SET_CHL: + num = copy_from_user(&val, argp, sizeof(int)); + addr = (unsigned char)(val>>1); + if (addr == pTAS2557->mnLAddr) { + dev_info(pTAS2557->dev, "TILOAD_IOCTL_SET_CHL left=0x%x\n", cmd); + pTAS2557->mnCurrentChannel = channel_left; + } else if (addr == pTAS2557->mnRAddr) { + dev_info(pTAS2557->dev, "TILOAD_IOCTL_SET_CHL right=0x%x\n", cmd); + pTAS2557->mnCurrentChannel = channel_right; + } else { + dev_err(pTAS2557->dev, "TILOAD_IOCTL_SET_CHL error L(0x%x) R(0x%x) 0x%x, cmd=0x%x\n", + pTAS2557->mnLAddr, pTAS2557->mnRAddr, addr, cmd); + } + break; + case TILOAD_IOCTL_SET_CONFIG: + dev_info(pTAS2557->dev, "TILOAD_IOCTL_SET_CONFIG=0x%x\n", cmd); + num = copy_from_user(&val, argp, sizeof(val)); + pTAS2557->set_config(pTAS2557, val); + break; + case TILOAD_IOCTL_SET_CALIBRATION: + dev_info(pTAS2557->dev, "TILOAD_IOCTL_SET_CALIBRATION=0x%x\n", cmd); + num = copy_from_user(&val, argp, sizeof(val)); + pTAS2557->set_calibration(pTAS2557, val); + break; + default: + dev_info(pTAS2557->dev, "%s, unsupport cmd=0x%x\n", __func__, cmd); + break; + } + return num; +} + +#ifdef CONFIG_COMPAT +static long tiload_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data; + long nResult = 0; + + switch (cmd) { + case TILOAD_COMPAT_IOMAGICNUM_GET: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_GET=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_GET, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_IOMAGICNUM_SET: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_SET=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_SET, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_BPR_READ: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_READ=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_BPR_READ, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_BPR_WRITE: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_WRITE=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_BPR_WRITE, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_IOCTL_SET_CHL: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CHL=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CHL, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_IOCTL_SET_CONFIG: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CONFIG=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CONFIG, + (unsigned long) compat_ptr(arg)); + break; + + case TILOAD_COMPAT_IOCTL_SET_CALIBRATION: + dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CALIBRATION=0x%x\n", + __func__, cmd); + nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CALIBRATION, + (unsigned long) compat_ptr(arg)); + break; + + default: + dev_err(pTAS2557->dev, "%s, unsupport compat ioctl=0x%x\n", + __func__, cmd); + break; + } + + return nResult; +} +#endif + +/* File operations structure for tiload */ +static const struct file_operations tiload_fops = { + .owner = THIS_MODULE, + .open = tiload_open, + .release = tiload_release, + .read = tiload_read, + .write = tiload_write, + .unlocked_ioctl = tiload_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tiload_compat_ioctl, +#endif +}; + +/* + * Function : tiload_driver_init + * Purpose : Register a char driver for dynamic tiload programming + */ +int tiload_driver_init(struct tas2557_priv *pTAS2557) +{ + int result; + dev_t dev = MKDEV(tiload_major, 0); + + g_TAS2557 = pTAS2557; + + dev_info(pTAS2557->dev, "%s\n", __func__); + + result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); + if (result < 0) { + dev_err(pTAS2557->dev, "cannot allocate major number %d\n", tiload_major); + return result; + } + tiload_class = class_create(DEVICE_NAME); + tiload_major = MAJOR(dev); + dev_info(pTAS2557->dev, "allocated Major Number: %d\n", tiload_major); + + tiload_cdev = cdev_alloc(); + cdev_init(tiload_cdev, &tiload_fops); + tiload_cdev->owner = THIS_MODULE; + tiload_cdev->ops = &tiload_fops; + + if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL) + dev_err(pTAS2557->dev, "Device creation failed\n"); + + if (cdev_add(tiload_cdev, dev, 1) < 0) { + dev_err(pTAS2557->dev, "tiload_driver: cdev_add failed\n"); + unregister_chrdev_region(dev, 1); + tiload_cdev = NULL; + return 1; + } + dev_info(pTAS2557->dev, "Registered TiLoad driver, Major number: %d\n", tiload_major); + /* class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); */ + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("Utility for TAS2557 Android in-system tuning"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2557_stereo/tiload.h b/sound/soc/codecs/tas2557_stereo/tiload.h new file mode 100644 index 00000000000000..f1f9c38c991c2a --- /dev/null +++ b/sound/soc/codecs/tas2557_stereo/tiload.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Texas Instruments Inc. + * Copyright (C) 2018 XiaoMi, 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: tiload.h + * Description: header file for tiload.c + */ + +#ifndef _TILOAD_H +#define _TILOAD_H + +#ifdef CONFIG_COMPAT +#include +#endif + +#include "tas2557.h" + +#define BPR_REG(book, page, reg) (((book * 256 * 128) + \ + (page * 128)) + reg) + +/* typedefs required for the included header files */ +struct BPR { + unsigned char nBook; + unsigned char nPage; + unsigned char nRegister; +}; + +/* defines */ +#define DEVICE_NAME "tiload_node" + +#define TILOAD_IOC_MAGIC 0xE0 +#define TILOAD_IOMAGICNUM_GET _IOR(TILOAD_IOC_MAGIC, 1, int) +#define TILOAD_IOMAGICNUM_SET _IOW(TILOAD_IOC_MAGIC, 2, int) +#define TILOAD_BPR_READ _IOR(TILOAD_IOC_MAGIC, 3, struct BPR) +#define TILOAD_BPR_WRITE _IOW(TILOAD_IOC_MAGIC, 4, struct BPR) +#define TILOAD_IOCTL_SET_CHL _IOW(TILOAD_IOC_MAGIC, 5, int) +#define TILOAD_IOCTL_SET_CONFIG _IOW(TILOAD_IOC_MAGIC, 6, int) +#define TILOAD_IOCTL_SET_CALIBRATION _IOW(TILOAD_IOC_MAGIC, 7, int) + +#ifdef CONFIG_COMPAT +#define TILOAD_COMPAT_IOMAGICNUM_GET _IOR(TILOAD_IOC_MAGIC, 1, compat_int_t) +#define TILOAD_COMPAT_IOMAGICNUM_SET _IOW(TILOAD_IOC_MAGIC, 2, compat_int_t) +#define TILOAD_COMPAT_BPR_READ _IOR(TILOAD_IOC_MAGIC, 3, struct BPR) +#define TILOAD_COMPAT_BPR_WRITE _IOW(TILOAD_IOC_MAGIC, 4, struct BPR) +#define TILOAD_COMPAT_IOCTL_SET_CHL _IOW(TILOAD_IOC_MAGIC, 5, compat_int_t) +#define TILOAD_COMPAT_IOCTL_SET_CONFIG _IOW(TILOAD_IOC_MAGIC, 6, compat_int_t) +#define TILOAD_COMPAT_IOCTL_SET_CALIBRATION _IOW(TILOAD_IOC_MAGIC, 7, compat_int_t) +#endif + +int tiload_driver_init(struct tas2557_priv *pTAS2557); + +#endif