summaryrefslogtreecommitdiff
path: root/asoc/codecs/tas256x/os_layer/src/tas256x-codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'asoc/codecs/tas256x/os_layer/src/tas256x-codec.c')
-rw-r--r--asoc/codecs/tas256x/os_layer/src/tas256x-codec.c1880
1 files changed, 1880 insertions, 0 deletions
diff --git a/asoc/codecs/tas256x/os_layer/src/tas256x-codec.c b/asoc/codecs/tas256x/os_layer/src/tas256x-codec.c
new file mode 100644
index 00000000..b228ead4
--- /dev/null
+++ b/asoc/codecs/tas256x/os_layer/src/tas256x-codec.c
@@ -0,0 +1,1880 @@
+/*
+ * =============================================================================
+ * Copyright (c) 2016 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
+ *
+ * File:
+ * tas256x-codec.c
+ *
+ * Description:
+ * ALSA SoC driver for Texas Instruments TAS256X High Performance 4W Smart
+ * Amplifier
+ *
+ * =============================================================================
+ */
+
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/version.h>
+
+#include "physical_layer/inc/tas256x.h"
+#include "physical_layer/inc/tas256x-device.h"
+#include "logical_layer/inc/tas256x-logic.h"
+#include "os_layer/inc/tas256x-regmap.h"
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+#include "algo/inc/tas_smart_amp_v2.h"
+#include "algo/inc/tas25xx-calib.h"
+#endif /*CONFIG_TAS25XX_ALGO*/
+
+#define TAS256X_MDELAY 0xFFFFFFFE
+#define TAS256X_MSLEEP 0xFFFFFFFD
+#define TAS256X_IVSENSER_ENABLE 1
+#define TAS256X_IVSENSER_DISABLE 0
+/* #define TAS2558_CODEC */
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static unsigned int tas256x_codec_read(struct snd_soc_component *codec,
+ unsigned int reg)
+{
+ unsigned int value = 0;
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ switch (reg) {
+ case TAS256X_LEFT_SWITCH:
+ value = p_tas256x->devs[0]->spk_control;
+ break;
+ case TAS256X_RIGHT_SWITCH:
+ value = p_tas256x->devs[1]->spk_control;
+ break;
+ case RX_SCFG_LEFT:
+ value = p_tas256x->devs[0]->rx_cfg;
+ break;
+ case RX_SCFG_RIGHT:
+ value = p_tas256x->devs[1]->rx_cfg;
+ break;
+ default:
+ ret = p_tas256x->read(p_tas256x, channel_left, reg,
+ &value);
+ break;
+ }
+
+ dev_dbg(plat_data->dev, "%s, reg=%d, value=%d", __func__, reg, value);
+
+ if (ret == 0)
+ return value;
+ else
+ return ret;
+}
+#else
+static unsigned int tas256x_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ unsigned int value = 0;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ switch (reg) {
+ case TAS256X_LEFT_SWITCH:
+ value = p_tas256x->devs[0]->spk_control;
+ break;
+ case TAS256X_RIGHT_SWITCH:
+ value = p_tas256x->devs[1]->spk_control;
+ break;
+ case RX_SCFG_LEFT:
+ value = p_tas256x->devs[0]->rx_cfg;
+ break;
+ case RX_SCFG_RIGHT:
+ value = p_tas256x->devs[1]->rx_cfg;
+ break;
+ default:
+ ret = p_tas256x->read(p_tas256x, channel_left, reg,
+ &value);
+ break;
+ }
+
+ dev_dbg(plat_data->dev, "%s, reg=%d, value=%d", __func__, reg, value);
+
+ if (ret == 0)
+ return value;
+ else
+ return ret;
+}
+#endif
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas256x_codec_write(struct snd_soc_component *codec,
+ unsigned int reg, unsigned int value)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ dev_dbg(plat_data->dev, "%s: %d, %d", __func__, reg, value);
+
+ switch (reg) {
+ case TAS256X_LEFT_SWITCH:
+ p_tas256x->devs[0]->spk_control = value;
+ break;
+ case TAS256X_RIGHT_SWITCH:
+ p_tas256x->devs[1]->spk_control = value;
+ break;
+ case RX_SCFG_LEFT:
+ ret = tas256x_update_rx_cfg(p_tas256x, value,
+ channel_left);
+ break;
+ case RX_SCFG_RIGHT:
+ ret = tas256x_update_rx_cfg(p_tas256x, value,
+ channel_right);
+ break;
+ default:
+ ret = p_tas256x->write(p_tas256x, channel_both,
+ reg, value);
+ break;
+ }
+
+ return ret;
+}
+#else
+static int tas256x_codec_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ dev_dbg(plat_data->dev, "%s: %d, %d", __func__, reg, value);
+
+ switch (reg) {
+ case TAS256X_LEFT_SWITCH:
+ p_tas256x->devs[0]->spk_control = value;
+ break;
+ case TAS256X_RIGHT_SWITCH:
+ p_tas256x->devs[1]->spk_control = value;
+ break;
+ case RX_SCFG_LEFT:
+ ret = tas256x_update_rx_cfg(p_tas256x, value,
+ channel_left);
+ break;
+ case RX_SCFG_RIGHT:
+ ret = tas256x_update_rx_cfg(p_tas256x, value,
+ channel_right);
+ break;
+ default:
+ ret = p_tas256x->write(p_tas256x, channel_both,
+ reg, value);
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#if IS_ENABLED(CODEC_PM)
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas256x_codec_suspend(struct snd_soc_component *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ mutex_lock(&p_tas256x->codec_lock);
+
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+ plat_data->runtime_suspend(p_tas256x);
+
+ mutex_unlock(&p_tas256x->codec_lock);
+ return ret;
+}
+
+static int tas256x_codec_resume(struct snd_soc_component *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ mutex_lock(&p_tas256x->codec_lock);
+
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+ plat_data->runtime_resume(p_tas256x);
+
+ mutex_unlock(&p_tas256x->codec_lock);
+ return ret;
+}
+#else
+static int tas256x_codec_suspend(struct snd_soc_codec *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ mutex_lock(&p_tas256x->codec_lock);
+
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+ plat_data->runtime_suspend(p_tas256x);
+
+ mutex_unlock(&p_tas256x->codec_lock);
+ return ret;
+}
+
+static int tas256x_codec_resume(struct snd_soc_codec *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+
+ mutex_lock(&p_tas256x->codec_lock);
+
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+ plat_data->runtime_resume(p_tas256x);
+
+ mutex_unlock(&p_tas256x->codec_lock);
+ return ret;
+}
+#endif
+#endif
+
+static int tas256x_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_info(plat_data->dev, "SND_SOC_DAPM_POST_PMU\n");
+ if (p_tas256x->mb_power_up == false)
+ tas256x_set_power_state(p_tas256x,
+ TAS256X_POWER_ACTIVE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_info(plat_data->dev, "SND_SOC_DAPM_PRE_PMD\n");
+ if (p_tas256x->mb_power_up == true)
+ tas256x_set_power_state(p_tas256x,
+ TAS256X_POWER_SHUTDOWN);
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const tas256x_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_left_enum, RX_SCFG_LEFT, 0,
+ tas256x_ASI1_src);
+static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_right_enum, RX_SCFG_RIGHT, 0,
+ tas256x_ASI1_src);
+
+static const struct snd_kcontrol_new dapm_switch_left =
+ SOC_DAPM_SINGLE("Switch", TAS256X_LEFT_SWITCH, 0, 1, 0);
+static const struct snd_kcontrol_new dapm_switch_right =
+ SOC_DAPM_SINGLE("Switch", TAS256X_RIGHT_SWITCH, 0, 1, 0);
+static const struct snd_kcontrol_new tas256x_asi1_left_mux =
+ SOC_DAPM_ENUM("Mux", tas2562_ASI1_src_left_enum);
+static const struct snd_kcontrol_new tas256x_asi1_right_mux =
+ SOC_DAPM_ENUM("Mux", tas2562_ASI1_src_right_enum);
+
+static const struct snd_soc_dapm_widget tas256x_dapm_widgets_stereo[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("TAS256X ASI Left", SND_SOC_NOPM, 0, 0,
+ &dapm_switch_left),
+ SND_SOC_DAPM_SWITCH("TAS256X ASI Right", SND_SOC_NOPM, 0, 0,
+ &dapm_switch_right),
+ SND_SOC_DAPM_MUX("TAS256X ASI1 SEL LEFT", SND_SOC_NOPM, 0, 0,
+ &tas256x_asi1_left_mux),
+ SND_SOC_DAPM_MUX("TAS256X ASI1 SEL RIGHT", SND_SOC_NOPM, 0, 0,
+ &tas256x_asi1_right_mux),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture", 1,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC1", NULL, SND_SOC_NOPM, 0, 0, tas256x_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", NULL, SND_SOC_NOPM, 0, 0, tas256x_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas256x_audio_map_stereo[] = {
+ {"TAS256X ASI1 SEL LEFT", "Left", "ASI1"},
+ {"TAS256X ASI1 SEL LEFT", "Right", "ASI1"},
+ {"TAS256X ASI1 SEL LEFT", "LeftRightDiv2", "ASI1"},
+ {"TAS256X ASI1 SEL LEFT", "I2C offset", "ASI1"},
+ {"TAS256X ASI1 SEL RIGHT", "Left", "ASI1"},
+ {"TAS256X ASI1 SEL RIGHT", "Right", "ASI1"},
+ {"TAS256X ASI1 SEL RIGHT", "LeftRightDiv2", "ASI1"},
+ {"TAS256X ASI1 SEL RIGHT", "I2C offset", "ASI1"},
+ {"DAC1", NULL, "TAS256X ASI1 SEL LEFT"},
+ {"DAC2", NULL, "TAS256X ASI1 SEL RIGHT"},
+ {"TAS256X ASI Left", "Switch", "DAC1"},
+ {"TAS256X ASI Right", "Switch", "DAC2"},
+ {"OUT1", NULL, "TAS256X ASI Left"},
+ {"OUT2", NULL, "TAS256X ASI Right"},
+ {"Voltage Sense", NULL, "VMON"},
+ {"Current Sense", NULL, "IMON"}
+};
+
+static const struct snd_soc_dapm_widget tas256x_dapm_widgets_mono[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("TAS256X ASI", SND_SOC_NOPM, 0, 0,
+ &dapm_switch_left),
+ SND_SOC_DAPM_MUX("TAS256X ASI1 SEL", SND_SOC_NOPM, 0, 0,
+ &tas256x_asi1_left_mux),
+ SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas256x_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas256x_audio_map_mono[] = {
+ {"TAS256X ASI1 SEL", "Left", "ASI1"},
+ {"TAS256X ASI1 SEL", "Right", "ASI1"},
+ {"TAS256X ASI1 SEL", "LeftRightDiv2", "ASI1"},
+ {"TAS256X ASI1 SEL", "I2C offset", "ASI1"},
+ {"DAC", NULL, "TAS256X ASI1 SEL"},
+ {"TAS256X ASI", "Switch", "DAC"},
+ {"OUT", NULL, "TAS256X ASI"},
+ {"Voltage Sense", NULL, "VMON"},
+ {"Current Sense", NULL, "IMON"}
+};
+
+static int tas256x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = dai->component;
+ struct tas256x_priv *p_tas256x
+ = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+ int bitwidth = 16;
+ int n_result = 0;
+ unsigned int channels = params_channels(params);
+
+ dev_dbg(plat_data->dev, "%s, stream %s format: %d\n", __func__,
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+ params_format(params));
+
+ mutex_lock(&p_tas256x->codec_lock);
+#ifndef TDM_MACHINE
+ /*Assumed TDM*/
+ if (channels > 2) {
+ p_tas256x->mn_fmt_mode = 2;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bitwidth = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bitwidth = 32;
+ break;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ n_result = tas256x_set_tdm_rx_slot(p_tas256x, channels, bitwidth);
+ else /*Assumed Capture*/
+ n_result = tas256x_set_tdm_tx_slot(p_tas256x, channels, bitwidth);
+ } else { /*Assumed I2S Mode*/
+ p_tas256x->mn_fmt_mode = 1;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bitwidth = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bitwidth = 32;
+ break;
+ }
+
+ n_result = tas256x_set_bitwidth(p_tas256x,
+ bitwidth, substream->stream);
+ if (n_result < 0) {
+ dev_err(plat_data->dev, "set bitwidth failed, %d\n",
+ n_result);
+ goto ret;
+ }
+ }
+#else
+ if (p_tas256x->mn_fmt_mode != 2) {
+ p_tas256x->mn_fmt_mode = 1;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bitwidth = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bitwidth = 32;
+ break;
+ }
+
+ n_result = tas256x_set_bitwidth(p_tas256x,
+ bitwidth, substream->stream);
+ if (n_result < 0) {
+ dev_err(plat_data->dev, "set bitwidth failed, %d\n",
+ n_result);
+ goto ret;
+ }
+ }
+#endif
+
+ dev_info(plat_data->dev, "%s, stream %s sample rate: %d\n", __func__,
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+ params_rate(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ n_result = tas256x_set_samplerate(p_tas256x,
+ params_rate(params), channel_both);
+
+ret:
+ mutex_unlock(&p_tas256x->codec_lock);
+ return n_result;
+}
+
+static int tas256x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = dai->component;
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+ int ret = 0;
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0, asi_cfg_2 = 0;
+
+ dev_dbg(plat_data->dev, "%s, format=0x%x\n", __func__, fmt);
+
+ p_tas256x->mn_fmt = 1;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ asi_cfg_1 = 0x00;
+ asi_cfg_2 = 0x00;
+ break;
+ default:
+ dev_err(plat_data->dev, "ASI format master is not found\n");
+ ret = -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(plat_data->dev, "INV format: NBNF\n");
+ asi_cfg_1 |= 0;
+ asi_cfg_2 |= 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(plat_data->dev, "INV format: IBNF\n");
+ asi_cfg_1 |= 1;
+ asi_cfg_2 |= 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(plat_data->dev, "INV format: NBIF\n");
+ asi_cfg_1 |= 0;
+ asi_cfg_2 |= 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(plat_data->dev, "INV format: IBIF\n");
+ asi_cfg_1 |= 1;
+ asi_cfg_2 |= 1;
+ break;
+ default:
+ dev_err(plat_data->dev, "ASI format Inverse is not found\n");
+ ret = -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case (SND_SOC_DAIFMT_I2S):
+ tdm_rx_start_slot = 1;
+ dev_dbg(plat_data->dev, " SND_SOC_DAIFMT_I2S tdm_rx_start_slot = 1\n");
+ break;
+ case (SND_SOC_DAIFMT_DSP_A):
+ tdm_rx_start_slot = 1;
+ dev_dbg(plat_data->dev, "SND_SOC_DAIFMT_DSP_A tdm_rx_start_slot =1\n");
+ break;
+ case (SND_SOC_DAIFMT_DSP_B):
+ tdm_rx_start_slot = 0;
+ dev_dbg(plat_data->dev, "SND_SOC_DAIFMT_DSP_B tdm_rx_start_slot = 0\n");
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J):
+ tdm_rx_start_slot = 0;
+ dev_dbg(plat_data->dev, "SND_SOC_DAIFMT_LEFT_J tdm_rx_start_slot = 0\n");
+ break;
+ default:
+ dev_err(plat_data->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = tas256x_rx_set_start_slot(p_tas256x,
+ tdm_rx_start_slot, channel_both);
+ if (ret)
+ goto end;
+
+ /*TX Offset is same as RX Offset*/
+ ret = tas256x_tx_set_start_slot(p_tas256x,
+ tdm_rx_start_slot, channel_both);
+ if (ret)
+ goto end;
+
+ ret = tas256x_rx_set_edge(p_tas256x,
+ asi_cfg_1, channel_both);
+ if (ret)
+ goto end;
+
+ /*TX Edge is reverse of RX Edge*/
+ ret = tas256x_tx_set_edge(p_tas256x,
+ !asi_cfg_1, channel_both);
+ if (ret)
+ goto end;
+
+ ret = tas256x_rx_set_frame_start(p_tas256x,
+ asi_cfg_2, channel_both);
+ if (ret)
+ goto end;
+
+end:
+ return ret;
+}
+
+static int tas256x_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ int ret = 0;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = dai->component;
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ dev_dbg(plat_data->dev, "%s, tx_mask:%d, rx_mask:%d",
+ __func__, tx_mask, rx_mask);
+ dev_dbg(plat_data->dev, "%s, slots:%d,slot_width:%d",
+ __func__, slots, slot_width);
+
+ if (rx_mask) {
+ p_tas256x->mn_fmt_mode = 2; /*TDM Mode*/
+ ret = tas256x_set_tdm_rx_slot(p_tas256x, slots, slot_width);
+ } else if (tx_mask) {
+ p_tas256x->mn_fmt_mode = 2;
+ ret = tas256x_set_tdm_tx_slot(p_tas256x, slots, slot_width);
+ } else {
+ dev_err(plat_data->dev, "%s, Invalid Mask",
+ __func__);
+ p_tas256x->mn_fmt_mode = 0;
+ }
+
+ return ret;
+}
+
+static int tas256x_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = dai->component;
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ dev_dbg(plat_data->dev, "%s, stream %s mute %d\n", __func__,
+ (stream == SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+ mute);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tas256x_dai_ops = {
+ .hw_params = tas256x_hw_params,
+ .set_fmt = tas256x_set_dai_fmt,
+ .set_tdm_slot = tas256x_set_dai_tdm_slot,
+ .mute_stream = tas256x_mute_stream,
+};
+
+#define TAS256X_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 tas256x_dai_driver[] = {
+ {
+ .name = "tas256x ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS256X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS256X_FORMATS,
+ },
+ .ops = &tas256x_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+/*Generic Control-1: IV Sense enable*/
+static char const *iv_enable_text[] = {"Off", "On"};
+static const struct soc_enum tas256x_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(iv_enable_text), iv_enable_text),
+};
+
+static int tas256xiv_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+#endif
+ struct tas256x_priv *p_tas256x = NULL;
+ int iv_enable = 0, n_result = 0;
+ struct linux_platform *plat_data = NULL;
+
+ if (codec == NULL) {
+ pr_err("%s:codec is NULL\n", __func__);
+ return 0;
+ }
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ if (p_tas256x == NULL) {
+ pr_err("%s:p_tas256x is NULL\n", __func__);
+ return 0;
+ }
+ plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ iv_enable = ucontrol->value.integer.value[0];
+
+ n_result = tas256x_iv_sense_enable_set(p_tas256x, iv_enable,
+ channel_both);
+
+ pr_debug("%s: tas256x->iv_enable = %d\n", __func__,
+ p_tas256x->iv_enable);
+
+ return n_result;
+}
+
+static int tas256xiv_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+#endif
+ struct tas256x_priv *p_tas256x = NULL;
+ struct linux_platform *plat_data = NULL;
+
+ if (codec == NULL) {
+ pr_err("%s:codec is NULL\n", __func__);
+ return 0;
+ }
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ if (p_tas256x == NULL) {
+ pr_err("%s:p_tas256x is NULL\n", __func__);
+ return 0;
+ }
+
+ plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ ucontrol->value.integer.value[0] =
+ tas256x_iv_sense_enable_get(p_tas256x, channel_left);
+ p_tas256x->iv_enable = ucontrol->value.integer.value[0];
+
+ dev_info(plat_data->dev, "p_tas256x->iv_enable %d\n",
+ p_tas256x->iv_enable);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tas256x_controls[] = {
+SOC_ENUM_EXT("TAS256X IVSENSE ENABLE", tas256x_enum[0],
+ tas256xiv_get, tas256xiv_put),
+};
+
+#if IS_ENABLED(CONFIG_TAS256X_REGBIN_PARSER)
+/* max. length of a alsa mixer control name */
+#define MAX_CONTROL_NAME 48
+
+static char *fw_name = "tas256x_reg.bin";
+
+const char *blocktype[5] = {
+ "COEFF",
+ "POST_POWER_UP",
+ "PRE_SHUTDOWN",
+ "PRE_POWER_UP",
+ "POST_SHUTDOWN"
+};
+
+static int tas256x_process_block(void *pContext, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct tas256x_priv *pTAS256x = (struct tas256x_priv *)pContext;
+ unsigned char subblk_typ = data[1];
+ int subblk_offset = 2;
+ enum channel chn = (0 == dev_idx) ? channel_both : (enum channel)dev_idx;
+
+ switch (subblk_typ) {
+ case TAS256X_CMD_SING_W: {
+/*
+ dev_idx : one byte
+ subblk_type : one byte
+ payload_len : two bytes
+ {
+ book : one byte
+ page : one byte
+ reg : one byte
+ val : one byte
+ }[payload_len/4]
+*/
+ int i = 0;
+ unsigned short len = SMS_HTONS(data[2], data[3]);
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ pr_err("Out of memory %s: %u\n", __func__, __LINE__);
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ pTAS256x->write(pTAS256x, chn,
+ TAS256X_REG(data[subblk_offset], data[subblk_offset + 1], data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TAS256X_CMD_BURST: {
+/*
+ dev_idx : one byte
+ subblk_type : one byte
+ payload_len : two bytes
+ book : one byte
+ page : one byte
+ reg : one byte
+ reserve : one byte
+ payload : payload_len bytes
+*/
+ unsigned short len = SMS_HTONS(data[2], data[3]);
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ pr_err("Out of memory %s: %u\n", __func__, __LINE__);
+ break;
+ }
+ if (len % 4) {
+ pr_err("Burst len is wrong %s: %u\n", __func__, __LINE__);
+ break;
+ }
+
+ pTAS256x->bulk_write(pTAS256x, chn,
+ TAS256X_REG(data[subblk_offset], data[subblk_offset + 1],
+ data[subblk_offset + 2]), &(data[subblk_offset + 4]), len);
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TAS256X_CMD_DELAY: {
+/*
+ dev_idx : one byte
+ subblk_type : one byte
+ delay_time : two bytes
+*/
+ unsigned short delay_time = 0;
+ if (subblk_offset + 2 > sublocksize) {
+ pr_err("Out of memory %s: %u\n", __func__, __LINE__);
+ break;
+ }
+ delay_time = SMS_HTONS(data[2], data[3]);
+ usleep_range(delay_time * 1000, delay_time * 1000);
+ subblk_offset += 2;
+ }
+ break;
+ case TAS256X_CMD_FIELD_W:
+/*
+ dev_idx : one byte
+ subblk_type : one byte
+ reserve : one byte
+ mask : one byte
+ book : one byte
+ page : one byte
+ reg : one byte
+ reserve : one byte
+ payload : payload_len bytes
+*/
+ if (subblk_offset + 6 > sublocksize) {
+ pr_err("Out of memory %s: %u\n", __func__, __LINE__);
+ break;
+ }
+ pTAS256x->update_bits(pTAS256x, chn,
+ TAS256X_REG(data[subblk_offset + 2], data[subblk_offset + 3], data[subblk_offset + 4]),
+ data[subblk_offset + 1], data[subblk_offset + 5]);
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ };
+
+ return subblk_offset;
+
+}
+
+void tas256x_select_cfg_blk(void *pContext, int conf_no, unsigned char block_type)
+{
+ struct tas256x_priv *pTAS256x = (struct tas256x_priv *) pContext;
+ struct tas256x_config_info **cfg_info = pTAS256x->cfg_info;
+ int i = 0, j = 0, k = 0;
+
+ if (conf_no > pTAS256x->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ pr_err("conf_no shoud be in range from 0 to %u\n", pTAS256x->ncfgs - 1);
+ goto EXIT;
+ } else {
+ pr_info("%s:%u:profile_conf_id = %d\n", __func__, __LINE__, conf_no);
+ }
+ for (i = 0; i < pTAS256x->ncfgs; i++) {
+ if (conf_no == i) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+ if (block_type > 5 || block_type < 2) {
+ pr_err("ERROR!!!block_type shoud be in range from 2 to 5\n");
+ goto EXIT;
+ }
+ if (block_type != cfg_info[i]->blk_data[j]->block_type)
+ continue;
+ pr_info("%s:%u:conf %d\n", __func__, __LINE__, i);
+ pr_info("%s:%u:block type:%s\t device idx = 0x%02x\n",
+ __func__, __LINE__, blocktype[cfg_info[i]->blk_data[j]->block_type - 1],
+ cfg_info[i]->blk_data[j]->dev_idx);
+ for (k = 0; k < (int)cfg_info[i]->blk_data[j]->nSublocks; k++) {
+ rc = tas256x_process_block(pTAS256x, cfg_info[i]->blk_data[j]->regdata + length,
+ cfg_info[i]->blk_data[j]->dev_idx, cfg_info[i]->blk_data[j]->block_size - length);
+ length += rc;
+ if (cfg_info[i]->blk_data[j]->block_size < length) {
+ pr_err("%s:%u:ERROR:%u %u out of memory\n", __func__, __LINE__,
+ length, cfg_info[i]->blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != cfg_info[i]->blk_data[j]->block_size) {
+ pr_err("%s:%u:ERROR: %u %u size is not same\n", __func__,
+ __LINE__, length, cfg_info[i]->blk_data[j]->block_size);
+ }
+ }
+ } else {
+ continue;
+ }
+ }
+EXIT:
+ return;
+}
+
+static struct tas256x_config_info *tas256x_add_config(unsigned char *config_data, unsigned int config_size)
+{
+ struct tas256x_config_info *cfg_info = NULL;
+ int config_offset = 0, i = 0;
+ cfg_info = (struct tas256x_config_info *)kzalloc(sizeof(struct tas256x_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ goto EXIT;
+ }
+ if (config_offset + 4 > (int)config_size) {
+ pr_err("%s:%u:Out of memory\n", __func__, __LINE__);
+ goto EXIT;
+ }
+ cfg_info->nblocks = SMS_HTONL(config_data[config_offset], config_data[config_offset + 1],
+ config_data[config_offset + 2], config_data[config_offset + 3]);
+ config_offset += 4;
+ pr_info("cfg_info->num_blocks = %u\n", cfg_info->nblocks);
+ cfg_info->blk_data = (struct tas256x_block_data **)kzalloc(
+ cfg_info->nblocks*sizeof(struct tas256x_block_data *),
+ GFP_KERNEL);
+ if (!cfg_info->blk_data) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ goto EXIT;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < (int)cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ pr_err("%s:%u:Out of memory: i = %d nblocks = %u!\n",
+ __func__, __LINE__, i, cfg_info->nblocks);
+ break;
+ }
+ cfg_info->blk_data[i] = (struct tas256x_block_data *)kzalloc(
+ sizeof(struct tas256x_block_data), GFP_KERNEL);
+ if (!cfg_info->blk_data[i]) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ break;
+ }
+ cfg_info->blk_data[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+ pr_info("blk_data(%d).dev_idx = 0x%02x\n", i,
+ cfg_info->blk_data[i]->dev_idx);
+ cfg_info->blk_data[i]->block_type = config_data[config_offset];
+ config_offset++;
+ pr_info("blk_data(%d).block_type = 0x%02x\n", i,
+ cfg_info->blk_data[i]->block_type);
+ cfg_info->blk_data[i]->yram_checksum = SMS_HTONS(config_data[config_offset],
+ config_data[config_offset + 1]);
+ config_offset += 2;
+ cfg_info->blk_data[i]->block_size = SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1], config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+ config_offset += 4;
+ pr_info("blk_data(%d).block_size = %u\n", i,
+ cfg_info->blk_data[i]->block_size);
+ cfg_info->blk_data[i]->nSublocks = SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1], config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+ pr_info("blk_data(%d).num_subblocks = %u\n", i,
+ cfg_info->blk_data[i]->nSublocks);
+ config_offset += 4;
+ pr_info("config_offset = %d\n", config_offset);
+ cfg_info->blk_data[i]->regdata = (unsigned char *)kzalloc(
+ cfg_info->blk_data[i]->block_size, GFP_KERNEL);
+ if (!cfg_info->blk_data[i]->regdata) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ goto EXIT;
+ }
+ if (config_offset + cfg_info->blk_data[i]->block_size > config_size) {
+ pr_err("%s:%u:Out of memory: i = %d nblocks = %u!\n",
+ __func__, __LINE__, i, cfg_info->nblocks);
+ break;
+ }
+ memcpy(cfg_info->blk_data[i]->regdata, &config_data[config_offset],
+ cfg_info->blk_data[i]->block_size);
+ config_offset += cfg_info->blk_data[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+EXIT:
+ return cfg_info;
+}
+
+static int tas256x_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_lock(&p_tas256x->codec_lock);
+#endif
+ uinfo->count = 1;
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_unlock(&p_tas256x->codec_lock);
+#endif
+ uinfo->value.integer.min = -1;
+ uinfo->value.integer.max = max(-1, p_tas256x->ncfgs - 1);
+ pr_info("%s: max profile = %d\n", __func__, (int)uinfo->value.integer.max);
+
+ return 0;
+}
+
+static int tas256x_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_lock(&p_tas256x->codec_lock);
+#endif
+ ucontrol->value.integer.value[0] = p_tas256x->profile_cfg_id;
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_unlock(&p_tas256x->codec_lock);
+#endif
+ return 0;
+}
+
+static int tas256x_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_lock(&p_tas256x->codec_lock);
+#endif
+ p_tas256x->profile_cfg_id = ucontrol->value.integer.value[0];
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_unlock(&p_tas256x->codec_lock);
+#endif
+
+ return 0;
+}
+
+static int tas256x_create_controls(struct tas256x_priv *pTAS256x)
+{
+ int nr_controls = 1, ret = 0, mix_index = 0;
+ char *name = NULL;
+ struct linux_platform *platform_data =
+ (struct linux_platform *)pTAS256x->platform_data;
+
+ struct snd_kcontrol_new *tas256x_profile_controls = NULL;
+ tas256x_profile_controls = devm_kzalloc(platform_data->dev,
+ nr_controls * sizeof(tas256x_profile_controls[0]), GFP_KERNEL);
+ if (NULL == tas256x_profile_controls) {
+ ret = -ENOMEM;
+ goto EXIT;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(platform_data->dev, MAX_CONTROL_NAME, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto EXIT;
+ }
+ scnprintf(name, MAX_CONTROL_NAME, "TAS256x Profile id");
+ tas256x_profile_controls[mix_index].name = name;
+ tas256x_profile_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ tas256x_profile_controls[mix_index].info = tas256x_info_profile;
+ tas256x_profile_controls[mix_index].get = tas256x_get_profile_id;
+ tas256x_profile_controls[mix_index].put = tas256x_set_profile_id;
+ mix_index++;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ ret = snd_soc_add_component_controls(platform_data->codec,
+ tas256x_profile_controls, nr_controls < mix_index ? nr_controls : mix_index);
+#else
+ ret = snd_soc_add_codec_controls(platform_data->codec,
+ tas256x_profile_controls, nr_controls < mix_index ? nr_controls : mix_index);
+#endif
+EXIT:
+ return ret;
+}
+
+static void tas256x_fw_ready(const struct firmware *pFW, void *pContext)
+{
+ struct tas256x_priv *pTAS256x = (struct tas256x_priv *) pContext;
+ struct tas256x_fw_hdr *fw_hdr = &(pTAS256x->fw_hdr);
+ struct tas256x_config_info **cfg_info = NULL;
+ unsigned char *buf = NULL;
+ int offset = 0, i = 0;
+ unsigned int total_config_sz = 0;
+
+ pTAS256x->fw_state = TAS256X_DSP_FW_FAIL;
+
+ if (unlikely(!pFW) || unlikely(!pFW->data)) {
+ pr_err("Failed to read %s, no side-effect on driver running\n", fw_name);
+ return;
+ }
+ buf = (unsigned char *)pFW->data;
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_lock(&pTAS256x->codec_lock);
+#endif
+#if IS_ENABLED(CONFIG_TAS256X_MISC)
+ mutex_lock(&pTAS256x->file_lock);
+#endif
+ pr_info("%s: start\n", __func__);
+ fw_hdr->img_sz = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (fw_hdr->img_sz != pFW->size) {
+ pr_err("File size not match, %d %u", (int)pFW->size, fw_hdr->img_sz);
+ goto EXIT;
+ }
+
+ fw_hdr->checksum = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->binnary_version_num = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->drv_fw_version = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->timestamp = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+
+ pr_info("ndev = %u\n", fw_hdr->ndev);
+
+ if (offset + TAS256X_DEVICE_SUM > fw_hdr->img_sz) {
+ pr_err("%s:%u:Out of Memory!\n", __func__, __LINE__);
+ goto EXIT;
+ }
+
+ for (i = 0; i < TAS256X_DEVICE_SUM; i++) {
+ fw_hdr->devs[i] = buf[offset];
+ offset += 1;
+ pr_info("devs[%d] = %u\n", i, fw_hdr->devs[i]);
+ }
+ fw_hdr->nconfig = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ pr_info("nconfig = %u\n", fw_hdr->nconfig);
+ for (i = 0; i < TAS256X_CONFIG_SIZE; i++) {
+ fw_hdr->config_size[i] = SMS_HTONL(buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ pr_info("config_size[%d] = %u\n", i, fw_hdr->config_size[i]);
+ total_config_sz += fw_hdr->config_size[i];
+ }
+ pr_info("img_sz = %u total_config_sz = %u offset = %d\n",
+ fw_hdr->img_sz, total_config_sz, offset);
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ pr_err("Bin file error!\n");
+ goto EXIT;
+ }
+ cfg_info = (struct tas256x_config_info **)kzalloc(
+ fw_hdr->nconfig*sizeof(struct tas256x_config_info *),
+ GFP_KERNEL);
+ if (!cfg_info) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ goto EXIT;
+ }
+ pTAS256x->cfg_info = cfg_info;
+ pTAS256x->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = tas256x_add_config(&buf[offset], fw_hdr->config_size[i]);
+ if (!cfg_info[i]) {
+ pr_err("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ pTAS256x->ncfgs += 1;
+ }
+
+ pTAS256x->fw_state = TAS256X_DSP_FW_OK;
+ tas256x_create_controls(pTAS256x);
+EXIT:
+#if IS_ENABLED(CONFIG_TAS256X_MISC)
+ mutex_unlock(&pTAS256x->file_lock);
+#endif
+#if IS_ENABLED(CONFIG_TAS256X_CODEC)
+ mutex_unlock(&pTAS256x->codec_lock);
+#endif
+ release_firmware(pFW);
+ pr_info("%s: Firmware init complete\n", __func__);
+ return;
+}
+
+int tas256x_load_container(struct tas256x_priv *pTAS256x)
+{
+ struct linux_platform *plat_data = NULL;
+
+ plat_data = (struct linux_platform *) pTAS256x->platform_data;
+ pTAS256x->fw_state = TAS256X_DSP_FW_PENDING;
+ return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fw_name, plat_data->dev, GFP_KERNEL, pTAS256x, tas256x_fw_ready);
+}
+
+void tas256x_config_info_remove(void *pContext)
+{
+ struct tas256x_priv *pTAS256x = (struct tas256x_priv *) pContext;
+ struct tas256x_config_info **cfg_info = pTAS256x->cfg_info;
+ int i = 0, j = 0;
+
+ if (cfg_info) {
+ for (i = 0; i < pTAS256x->ncfgs; i++) {
+ if (cfg_info[i]) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ if (cfg_info[i]->blk_data[j]->regdata)
+ kfree(cfg_info[i]->blk_data[j]->regdata);
+ if (cfg_info[i]->blk_data[j])
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ if (cfg_info[i]->blk_data)
+ kfree(cfg_info[i]->blk_data);
+ kfree(cfg_info[i]);
+ }
+ }
+ kfree(cfg_info);
+ }
+}
+#endif
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static int tas256x_codec_probe(struct snd_soc_component *codec)
+{
+ int ret, i;
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(codec);
+
+ if (plat_data)
+ plat_data->codec = codec;
+
+ ret = snd_soc_add_component_controls(codec, tas256x_controls,
+ ARRAY_SIZE(tas256x_controls));
+ if (ret < 0) {
+ pr_err("%s: add_codec_controls failed, err %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_ignore_suspend(dapm, "ASI1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "ASI1 Capture");
+ if (p_tas256x->mn_channels == 2) {
+ snd_soc_dapm_ignore_suspend(dapm, "OUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "OUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "VMON");
+ snd_soc_dapm_ignore_suspend(dapm, "IMON");
+ } else {
+ snd_soc_dapm_ignore_suspend(dapm, "OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "VMON");
+ snd_soc_dapm_ignore_suspend(dapm, "IMON");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ for (i = 0; i < p_tas256x->mn_channels; i++) {
+ if (p_tas256x->devs[i]->dev_ops.tas_probe)
+ ret |= (p_tas256x->devs[i]->dev_ops.tas_probe)(p_tas256x, codec, i + 1);
+ }
+
+ /* Generic Probe */
+ ret = tas256x_probe(p_tas256x);
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+
+ return 0;
+}
+#else
+static int tas256x_codec_probe(struct snd_soc_codec *codec)
+{
+ int ret, i;
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+
+ if (plat_data)
+ plat_data->codec = codec;
+
+ dev_info(plat_data->dev, "Driver Tag: %s\n", TAS256X_DRIVER_TAG);
+ ret = snd_soc_add_codec_controls(codec, tas256x_controls,
+ ARRAY_SIZE(tas256x_controls));
+ if (ret < 0) {
+ pr_err("%s: add_codec_controls failed, err %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_ignore_suspend(dapm, "ASI1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "ASI1 Capture");
+ if (p_tas256x->mn_channels == 2) {
+ snd_soc_dapm_ignore_suspend(dapm, "OUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "OUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "VMON");
+ snd_soc_dapm_ignore_suspend(dapm, "IMON");
+ } else {
+ snd_soc_dapm_ignore_suspend(dapm, "OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "VMON");
+ snd_soc_dapm_ignore_suspend(dapm, "IMON");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ for (i = 0; i < p_tas256x->mn_channels; i++) {
+ if (p_tas256x->devs[i]->dev_ops.tas_probe)
+ ret |= (p_tas256x->devs[i]->dev_ops.tas_probe)(p_tas256x, codec, i + 1);
+ }
+ /* Generic Probe */
+ ret = tas256x_probe(p_tas256x);
+ dev_dbg(plat_data->dev, "%s\n", __func__);
+
+ return ret;
+}
+#endif
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static void tas256x_codec_remove(struct snd_soc_component *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+
+ tas256x_remove(p_tas256x);
+}
+#else
+static int tas256x_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+
+ tas256x_remove(p_tas256x);
+ return 0;
+}
+#endif
+
+/*snd control-1: SmartPA System Mute(Master) Control*/
+static int tas256x_system_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ pValue->value.integer.value[0] = p_tas256x->mb_mute;
+ dev_dbg(plat_data->dev, "%s = %d\n",
+ __func__, p_tas256x->mb_mute);
+
+ return 0;
+}
+
+static int tas256x_system_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int mb_mute = pValue->value.integer.value[0];
+
+ dev_dbg(plat_data->dev, "%s = %d\n", __func__, mb_mute);
+
+ p_tas256x->mb_mute = !!mb_mute;
+
+ return 0;
+}
+
+/*snd control-2: SmartPA Mute Control*/
+static int tas256x_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ pValue->value.integer.value[0] = p_tas256x->mb_mute;
+
+ if ((p_tas256x->mb_power_up == true) &&
+ (p_tas256x->mn_power_state == TAS256X_POWER_ACTIVE))
+ pValue->value.integer.value[0] = 0;
+ else
+ pValue->value.integer.value[0] = 1;
+
+ dev_dbg(plat_data->dev, "%s = %ld\n",
+ __func__, pValue->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tas256x_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int mute = pValue->value.integer.value[0];
+
+ dev_dbg(plat_data->dev, "%s, %d\n", __func__, mute);
+ mutex_lock(&p_tas256x->codec_lock);
+
+ if (mute)
+ tas256x_set_power_state(p_tas256x, TAS256X_POWER_MUTE);
+ else
+ tas256x_set_power_state(p_tas256x, TAS256X_POWER_ACTIVE);
+
+ mutex_unlock(&p_tas256x->codec_lock);
+ return 0;
+}
+
+/*snd control-3: DAC Mute Control*/
+static int tas256x_dac_mute_ctrl_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ pValue->value.integer.value[0] = p_tas256x->dac_mute;
+
+ dev_dbg(plat_data->dev, "%s = %ld\n",
+ __func__, pValue->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tas256x_dac_mute_ctrl_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ int n_result = 0;
+ enum channel chn = channel_left;
+ int mute = pValue->value.integer.value[0];
+ int i = 0, chnTemp = 0;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ dev_dbg(plat_data->dev, "%s, %d\n", __func__, mute);
+ mutex_lock(&p_tas256x->codec_lock);
+
+ for (i = 0; i < p_tas256x->mn_channels; i++) {
+ if (p_tas256x->devs[i]->spk_control == 1)
+ chnTemp |= 1 << i;
+ }
+ chn = (chnTemp == 0) ? chn : (enum channel)chnTemp;
+
+ if (mute) {
+ n_result = tas256x_set_power_mute(p_tas256x, chn);
+ } else {
+ msleep(50);
+#if IS_ENABLED(CONFIG_TAS256X_REGBIN_PARSER)
+ /*set p_tas256x->profile_cfg_id by tinymix*/
+ tas256x_select_cfg_blk(p_tas256x, p_tas256x->profile_cfg_id,
+ TAS256X_BIN_BLK_PRE_POWER_UP);
+#endif
+ n_result = tas256x_set_power_up(p_tas256x, chn);
+#if IS_ENABLED(CONFIG_TAS256X_REGBIN_PARSER)
+ /*set p_tas256x->profile_cfg_id by tinymix*/
+ tas256x_select_cfg_blk(p_tas256x, p_tas256x->profile_cfg_id,
+ TAS256X_BIN_BLK_POST_POWER_UP);
+#endif
+ }
+
+ p_tas256x->dac_mute = mute;
+ mutex_unlock(&p_tas256x->codec_lock);
+
+ return n_result;
+}
+
+/*ICN Disable*/
+static const char * const icn_sw_text[] = {"Enable", "Disable"};
+static const struct soc_enum icn_sw_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(icn_sw_text),
+ icn_sw_text),
+};
+static int tas256x_get_icn_switch(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *p_u_control)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ dev_info(plat_data->dev, "%s, icn_sw = %ld\n",
+ __func__, p_u_control->value.integer.value[0]);
+ p_u_control->value.integer.value[0] = p_tas256x->icn_sw;
+ return 0;
+}
+static int tas256x_set_icn_switch(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *p_u_control)
+{
+ int ret = 0;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+
+ p_tas256x->icn_sw = p_u_control->value.integer.value[0];
+ ret = tas256x_icn_disable(p_tas256x, p_tas256x->icn_sw, channel_both);
+
+ return ret;
+}
+
+/*Rx Slot*/
+static int tas256x_set_rx_slot_map_single(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ int ret = 0;
+ int value = 0;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ value = pValue->value.integer.value[0];
+
+ ret = tas256x_rx_set_slot(p_tas256x, value, channel_left);
+ dev_dbg(plat_data->dev, "%s = %ld\n",
+ __func__, pValue->value.integer.value[0]);
+
+ return ret;
+}
+
+static int tas256x_get_rx_slot_map_single(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data = (struct linux_platform *) p_tas256x->platform_data;
+
+ pValue->value.integer.value[0] = p_tas256x->mn_rx_slot_map[0];
+ dev_dbg(plat_data->dev, "%s = %ld\n",
+ __func__, pValue->value.integer.value[0]);
+ return 0;
+}
+
+static const struct snd_kcontrol_new tas256x_snd_controls_mono[] = {
+ SOC_SINGLE_EXT("SmartPA System Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_system_mute_ctrl_get, tas256x_system_mute_ctrl_put),
+ SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_mute_ctrl_get, tas256x_mute_ctrl_put),
+ SOC_SINGLE_EXT("TAS256X DAC Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_dac_mute_ctrl_get, tas256x_dac_mute_ctrl_put),
+ SOC_ENUM_EXT("TAS256X ICN Switch", icn_sw_enum[0],
+ tas256x_get_icn_switch,
+ tas256x_set_icn_switch),
+ SOC_SINGLE_EXT("TAS256X_RX_SLOT_MAP", SND_SOC_NOPM, 0, 7, 0,
+ tas256x_get_rx_slot_map_single,
+ tas256x_set_rx_slot_map_single),
+};
+
+struct soc_multi_control_ch_map {
+ int min, max, platform_max, count;
+ unsigned int reg, rreg, shift, rshift, invert;
+};
+
+int tas256x_rx_slot_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_multi_control_ch_map *mc =
+ (struct soc_multi_control_ch_map *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = mc->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->platform_max;
+
+ return 0;
+}
+
+static int tas256x_get_rx_slot_map_multi(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int i;
+ for (i = 0; i < 2; i++) {
+ dev_info(plat_data->dev, "%s idx=%d, value=%d\n",
+ __func__, i, (int)p_tas256x->mn_rx_slot_map[i]);
+ ucontrol->value.integer.value[i] =
+ (unsigned int) p_tas256x->mn_rx_slot_map[i];
+ }
+
+ return 0;
+}
+
+static int tas256x_set_rx_slot_map_multi(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_component_get_drvdata(codec);
+#else
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tas256x_priv *p_tas256x = snd_soc_codec_get_drvdata(codec);
+#endif
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+ int i, ret = 0;
+ char slot_map[2];
+
+ for (i = 0; i < 2; i++) {
+ slot_map[i] = (char)(ucontrol->value.integer.value[i]);
+ ret = tas256x_rx_set_slot(p_tas256x, slot_map[i], i+1);
+ dev_info(plat_data->dev, "%s mapping - index %d = channel %d\n",
+ __func__, i, slot_map[i]);
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new tas256x_snd_controls_stereo[] = {
+ SOC_SINGLE_EXT("SmartPA System Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_system_mute_ctrl_get, tas256x_system_mute_ctrl_put),
+ SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_mute_ctrl_get, tas256x_mute_ctrl_put),
+ SOC_SINGLE_EXT("TAS256X DAC Mute", SND_SOC_NOPM, 0, 0x0001, 0,
+ tas256x_dac_mute_ctrl_get, tas256x_dac_mute_ctrl_put),
+ SOC_ENUM_EXT("TAS256X ICN Switch", icn_sw_enum[0],
+ tas256x_get_icn_switch,
+ tas256x_set_icn_switch),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "TAS256X_RX_SLOT_MAP",
+ .info = tas256x_rx_slot_map_info,
+ .get = tas256x_get_rx_slot_map_multi,
+ .put = tas256x_set_rx_slot_map_multi,
+ .private_value = (unsigned long) &(struct soc_multi_control_ch_map) {
+ .reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 7,
+ .count = 2,
+ .platform_max = 7,
+ .invert = 0,
+ }
+ },
+};
+
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+static struct snd_soc_component_driver soc_codec_driver_tas256x = {
+ .probe = tas256x_codec_probe,
+ .remove = tas256x_codec_remove,
+ .read = tas256x_codec_read,
+ .write = tas256x_codec_write,
+#if IS_ENABLED(CODEC_PM)
+ .suspend = tas256x_codec_suspend,
+ .resume = tas256x_codec_resume,
+#endif
+ .controls = tas256x_snd_controls_mono,
+ .num_controls = ARRAY_SIZE(tas256x_snd_controls_mono),
+ .dapm_widgets = tas256x_dapm_widgets_mono,
+ .num_dapm_widgets = ARRAY_SIZE(tas256x_dapm_widgets_mono),
+ .dapm_routes = tas256x_audio_map_mono,
+ .num_dapm_routes = ARRAY_SIZE(tas256x_audio_map_mono),
+};
+#else
+static struct snd_soc_codec_driver soc_codec_driver_tas256x = {
+ .probe = tas256x_codec_probe,
+ .remove = tas256x_codec_remove,
+ .read = tas256x_codec_read,
+ .write = tas256x_codec_write,
+#if IS_ENABLED(CODEC_PM)
+ .suspend = tas256x_codec_suspend,
+ .resume = tas256x_codec_resume,
+#endif
+ .component_driver = {
+ .controls = tas256x_snd_controls_mono,
+ .num_controls = ARRAY_SIZE(tas256x_snd_controls_mono),
+ .dapm_widgets = tas256x_dapm_widgets_mono,
+ .num_dapm_widgets = ARRAY_SIZE(tas256x_dapm_widgets_mono),
+ .dapm_routes = tas256x_audio_map_mono,
+ .num_dapm_routes = ARRAY_SIZE(tas256x_audio_map_mono),
+ },
+};
+#endif
+
+int tas256x_register_codec(struct tas256x_priv *p_tas256x)
+{
+ int n_result = 0;
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+
+ dev_info(plat_data->dev, "%s, enter\n", __func__);
+
+ if (p_tas256x->mn_channels == 2) {
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ soc_codec_driver_tas256x.controls =
+ tas256x_snd_controls_stereo;
+ soc_codec_driver_tas256x.num_controls =
+ ARRAY_SIZE(tas256x_snd_controls_stereo);
+ soc_codec_driver_tas256x.dapm_widgets =
+ tas256x_dapm_widgets_stereo;
+ soc_codec_driver_tas256x.num_dapm_widgets =
+ ARRAY_SIZE(tas256x_dapm_widgets_stereo);
+ soc_codec_driver_tas256x.dapm_routes =
+ tas256x_audio_map_stereo;
+ soc_codec_driver_tas256x.num_dapm_routes =
+ ARRAY_SIZE(tas256x_audio_map_stereo);
+ n_result = devm_snd_soc_register_component(plat_data->dev,
+ &soc_codec_driver_tas256x,
+ tas256x_dai_driver, ARRAY_SIZE(tas256x_dai_driver));
+#else
+ soc_codec_driver_tas256x.component_driver.controls =
+ tas256x_snd_controls_stereo;
+ soc_codec_driver_tas256x.component_driver.num_controls =
+ ARRAY_SIZE(tas256x_snd_controls_stereo);
+ soc_codec_driver_tas256x.component_driver.dapm_widgets =
+ tas256x_dapm_widgets_stereo;
+ soc_codec_driver_tas256x.component_driver.num_dapm_widgets =
+ ARRAY_SIZE(tas256x_dapm_widgets_stereo);
+ soc_codec_driver_tas256x.component_driver.dapm_routes =
+ tas256x_audio_map_stereo;
+ soc_codec_driver_tas256x.component_driver.num_dapm_routes =
+ ARRAY_SIZE(tas256x_audio_map_stereo);
+ n_result = snd_soc_register_codec(plat_data->dev,
+ &soc_codec_driver_tas256x,
+ tas256x_dai_driver, ARRAY_SIZE(tas256x_dai_driver));
+#endif
+ } else {
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ n_result = devm_snd_soc_register_component(plat_data->dev,
+ &soc_codec_driver_tas256x,
+ tas256x_dai_driver, ARRAY_SIZE(tas256x_dai_driver));
+#else
+ n_result = snd_soc_register_codec(plat_data->dev,
+ &soc_codec_driver_tas256x,
+ tas256x_dai_driver, ARRAY_SIZE(tas256x_dai_driver));
+#endif
+ }
+ return n_result;
+}
+
+int tas256x_deregister_codec(struct tas256x_priv *p_tas256x)
+{
+ struct linux_platform *plat_data =
+ (struct linux_platform *) p_tas256x->platform_data;
+#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
+ snd_soc_unregister_component(plat_data->dev);
+#else
+ snd_soc_unregister_codec(plat_data->dev);
+#endif
+ return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS256X ALSA SOC Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+#endif /* CONFIG_TAS256X_CODEC */