ASoC: tegra-alt: Reset T210 driver modules

1. Add soft reset for I2S, ADMAIF, AMX and ADX modules
    to handle successive start/stop scenarios
2. Flush AMX/ADX map table before sucessive mapping changes
3. Restore tx/rx_crtl, tx/rx_cif_crtl, i2s_ctrl
    and offset after soft reset

Bug 1442940

Change-Id: Id275fccf32857f897080f40ec2d9f25a532c262f
Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com>
Signed-off-by: Junghyun Kim <juskim@nvidia.com>
Reviewed-on: http://git-master/r/457198
This commit is contained in:
Arun Shamanna Lakshmi
2014-08-15 12:00:18 -07:00
committed by Sameer Pujar
parent 8f48438124
commit 684a5ee845
4 changed files with 347 additions and 17 deletions

View File

@@ -15,6 +15,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
@@ -154,6 +156,25 @@ static int tegra210_admaif_sw_reset(struct snd_soc_dai *dai,
return 0; return 0;
} }
static int tegra210_admaif_get_status(struct snd_soc_dai *dai,
int direction)
{
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
unsigned int status_reg, val;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
status_reg = TEGRA210_ADMAIF_XBAR_RX_STATUS +
(dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
} else {
status_reg = TEGRA210_ADMAIF_XBAR_TX_STATUS +
(dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
}
regmap_read(admaif->regmap, status_reg, &val);
val = (val & 0x00000001);
return val;
}
static int tegra210_admaif_runtime_suspend(struct device *dev) static int tegra210_admaif_runtime_suspend(struct device *dev)
{ {
struct tegra210_admaif *admaif = dev_get_drvdata(dev); struct tegra210_admaif *admaif = dev_get_drvdata(dev);
@@ -224,7 +245,7 @@ static int tegra210_admaif_hw_params(struct snd_pcm_substream *substream,
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai); struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
struct tegra210_xbar_cif_conf cif_conf; struct tegra210_xbar_cif_conf cif_conf;
unsigned int reg, fifo_ctrl, fifo_size; unsigned int reg, fifo_ctrl, fifo_size;
int valid_bit, ret; int valid_bit;
cif_conf.audio_channels = params_channels(params); cif_conf.audio_channels = params_channels(params);
cif_conf.client_channels = params_channels(params); cif_conf.client_channels = params_channels(params);
@@ -266,13 +287,6 @@ static int tegra210_admaif_hw_params(struct snd_pcm_substream *substream,
fifo_size = 3; fifo_size = 3;
} }
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_admaif_sw_reset(dai, substream->stream, 0xffff);
if (ret) {
dev_err(dev, "Failed at sw reset\n");
return ret;
}
tegra210_admaif_set_pack_mode(admaif->regmap, reg, valid_bit); tegra210_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
admaif->soc_data->set_audio_cif(admaif->regmap, reg, &cif_conf); admaif->soc_data->set_audio_cif(admaif->regmap, reg, &cif_conf);
@@ -304,8 +318,10 @@ static void tegra210_admaif_start_playback(struct snd_soc_dai *dai)
static void tegra210_admaif_stop_playback(struct snd_soc_dai *dai) static void tegra210_admaif_stop_playback(struct snd_soc_dai *dai)
{ {
struct device *dev = dai->dev;
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai); struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
unsigned int reg; unsigned int reg;
int dcnt = 10, ret;
tegra210_admaif_global_enable(admaif, 0); tegra210_admaif_global_enable(admaif, 0);
reg = TEGRA210_ADMAIF_XBAR_TX_ENABLE + reg = TEGRA210_ADMAIF_XBAR_TX_ENABLE +
@@ -313,6 +329,16 @@ static void tegra210_admaif_stop_playback(struct snd_soc_dai *dai)
regmap_update_bits(admaif->regmap, reg, regmap_update_bits(admaif->regmap, reg,
TEGRA210_ADMAIF_XBAR_TX_ENABLE_MASK, TEGRA210_ADMAIF_XBAR_TX_ENABLE_MASK,
0); 0);
/* wait until ADMAIF TX status is disabled */
while (tegra210_admaif_get_status(dai, SNDRV_PCM_STREAM_PLAYBACK) &&
dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_admaif_sw_reset(dai, SNDRV_PCM_STREAM_PLAYBACK, 0xffff);
if (ret)
dev_err(dev, "Failed at ADMAIF%d_TX sw reset\n", dev->id);
} }
static void tegra210_admaif_start_capture(struct snd_soc_dai *dai) static void tegra210_admaif_start_capture(struct snd_soc_dai *dai)
@@ -330,8 +356,10 @@ static void tegra210_admaif_start_capture(struct snd_soc_dai *dai)
static void tegra210_admaif_stop_capture(struct snd_soc_dai *dai) static void tegra210_admaif_stop_capture(struct snd_soc_dai *dai)
{ {
struct device *dev = dai->dev;
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai); struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
unsigned int reg; unsigned int reg;
int dcnt = 10, ret;
tegra210_admaif_global_enable(admaif, 0); tegra210_admaif_global_enable(admaif, 0);
reg = TEGRA210_ADMAIF_XBAR_RX_ENABLE + reg = TEGRA210_ADMAIF_XBAR_RX_ENABLE +
@@ -339,6 +367,16 @@ static void tegra210_admaif_stop_capture(struct snd_soc_dai *dai)
regmap_update_bits(admaif->regmap, reg, regmap_update_bits(admaif->regmap, reg,
TEGRA210_ADMAIF_XBAR_RX_ENABLE_MASK, TEGRA210_ADMAIF_XBAR_RX_ENABLE_MASK,
0); 0);
/* wait until ADMAIF RX status is disabled */
while (tegra210_admaif_get_status(dai, SNDRV_PCM_STREAM_CAPTURE) &&
dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_admaif_sw_reset(dai, SNDRV_PCM_STREAM_CAPTURE, 0xffff);
if (ret)
dev_err(dev, "Failed at ADMAIF%d_RX sw reset\n", dev->id);
} }
static int tegra210_admaif_trigger(struct snd_pcm_substream *substream, int cmd, static int tegra210_admaif_trigger(struct snd_pcm_substream *substream, int cmd,

View File

@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
@@ -142,6 +143,62 @@ static void tegra210_adx_update_map_ram(struct tegra210_adx *adx)
tegra210_adx_write_map_ram(adx, i, adx->map[i]); tegra210_adx_write_map_ram(adx, i, adx->map[i]);
} }
static int tegra210_adx_sw_reset(struct tegra210_adx *adx,
int timeout)
{
unsigned int val;
int wait = timeout;
regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_ADX_SOFT_RESET_SOFT_EN);
do {
regmap_read(adx->regmap, TEGRA210_ADX_SOFT_RESET, &val);
wait--;
if (!wait)
return -EINVAL;
} while (val & 0x00000001);
regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT);
return 0;
}
static int tegra210_adx_get_status(struct tegra210_adx *adx)
{
unsigned int val;
regmap_read(adx->regmap, TEGRA210_ADX_STATUS, &val);
val = (val & 0x00000001);
return val;
}
static int tegra210_adx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct device *dev = codec->dev;
struct tegra210_adx *adx = dev_get_drvdata(dev);
int dcnt = 10, ret = 0;
/* wait until ADX status is disabled */
while (tegra210_adx_get_status(adx) && dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_adx_sw_reset(adx, 0xffff);
if (ret) {
dev_err(dev, "Failed at ADX%d sw reset\n", dev->id);
return ret;
}
return (dcnt < 0) ? -ETIMEDOUT : 0;
}
#ifdef TEGRA210_ADX_MAP_READ #ifdef TEGRA210_ADX_MAP_READ
static unsigned int tegra210_adx_read_map_ram(struct tegra210_adx *adx, static unsigned int tegra210_adx_read_map_ram(struct tegra210_adx *adx,
unsigned int addr) unsigned int addr)
@@ -301,7 +358,14 @@ int tegra210_adx_set_channel_map(struct snd_soc_dai *dai,
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
unsigned int byte_mask1 = 0, byte_mask2 = 0; unsigned int byte_mask1 = 0, byte_mask2 = 0;
unsigned int out_stream_idx, out_ch_idx, out_byte_idx; unsigned int out_stream_idx, out_ch_idx, out_byte_idx;
int i; int i, ret = 0;
/* HW needs sw reset to make sure previous transaction be clean */
tegra210_adx_sw_reset(adx, 0xffff);
if (ret) {
dev_err(dev, "Failed at ADX sw reset\n");
return ret;
}
if ((rx_num < 1) || (rx_num > 64)) { if ((rx_num < 1) || (rx_num > 64)) {
dev_err(dev, "Doesn't support %d rx_num, need to be 1 to 64\n", dev_err(dev, "Doesn't support %d rx_num, need to be 1 to 64\n",
@@ -402,7 +466,9 @@ static struct snd_soc_dai_driver tegra210_adx_dais[] = {
}; };
static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = { static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
SND_SOC_DAPM_AIF_IN("IN", NULL, 0, TEGRA210_ADX_ENABLE, 0, 0), SND_SOC_DAPM_AIF_IN_E("IN", NULL, 0, TEGRA210_ADX_ENABLE,
TEGRA210_ADX_ENABLE_SHIFT, 0,
tegra210_adx_stop, SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0), SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0), SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0), SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
@@ -481,6 +547,8 @@ static bool tegra210_adx_rd_reg(struct device *dev,
case TEGRA210_ADX_ENABLE: case TEGRA210_ADX_ENABLE:
case TEGRA210_ADX_SOFT_RESET: case TEGRA210_ADX_SOFT_RESET:
case TEGRA210_ADX_CG: case TEGRA210_ADX_CG:
case TEGRA210_ADX_STATUS:
case TEGRA210_ADX_INT_STATUS:
case TEGRA210_ADX_CTRL: case TEGRA210_ADX_CTRL:
case TEGRA210_ADX_IN_BYTE_EN0: case TEGRA210_ADX_IN_BYTE_EN0:
case TEGRA210_ADX_IN_BYTE_EN1: case TEGRA210_ADX_IN_BYTE_EN1:
@@ -493,6 +561,29 @@ static bool tegra210_adx_rd_reg(struct device *dev,
}; };
} }
static bool tegra210_adx_volatile_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_ADX_AXBAR_RX_STATUS:
case TEGRA210_ADX_AXBAR_RX_INT_STATUS:
case TEGRA210_ADX_AXBAR_RX_INT_SET:
case TEGRA210_ADX_AXBAR_TX_STATUS:
case TEGRA210_ADX_AXBAR_TX_INT_STATUS:
case TEGRA210_ADX_AXBAR_TX_INT_SET:
case TEGRA210_ADX_SOFT_RESET:
case TEGRA210_ADX_STATUS:
case TEGRA210_ADX_INT_STATUS:
case TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL:
case TEGRA210_ADX_AHUBRAMCTL_ADX_DATA:
return true;
default:
break;
};
return false;
}
static const struct regmap_config tegra210_adx_regmap_config = { static const struct regmap_config tegra210_adx_regmap_config = {
.reg_bits = 32, .reg_bits = 32,
.reg_stride = 4, .reg_stride = 4,
@@ -500,6 +591,7 @@ static const struct regmap_config tegra210_adx_regmap_config = {
.max_register = TEGRA210_ADX_AHUBRAMCTL_ADX_DATA, .max_register = TEGRA210_ADX_AHUBRAMCTL_ADX_DATA,
.writeable_reg = tegra210_adx_wr_reg, .writeable_reg = tegra210_adx_wr_reg,
.readable_reg = tegra210_adx_rd_reg, .readable_reg = tegra210_adx_rd_reg,
.volatile_reg = tegra210_adx_volatile_reg,
.cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE,
}; };

View File

@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
@@ -167,6 +168,62 @@ static void tegra210_amx_update_map_ram(struct tegra210_amx *amx)
tegra210_amx_write_map_ram(amx, i, amx->map[i]); tegra210_amx_write_map_ram(amx, i, amx->map[i]);
} }
static int tegra210_amx_sw_reset(struct tegra210_amx *amx,
int timeout)
{
unsigned int val;
int wait = timeout;
regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_AMX_SOFT_RESET_SOFT_EN);
do {
regmap_read(amx->regmap, TEGRA210_AMX_SOFT_RESET, &val);
wait--;
if (!wait)
return -EINVAL;
} while (val & 0x00000001);
regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_AMX_SOFT_RESET_SOFT_DEFAULT);
return 0;
}
static int tegra210_amx_get_status(struct tegra210_amx *amx)
{
unsigned int val;
regmap_read(amx->regmap, TEGRA210_AMX_STATUS, &val);
val = (val & 0x00000001);
return val;
}
static int tegra210_amx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct device *dev = codec->dev;
struct tegra210_amx *amx = dev_get_drvdata(dev);
int dcnt = 10, ret = 0;
/* wait until AMX status is disabled */
while (tegra210_amx_get_status(amx) && dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_amx_sw_reset(amx, 0xffff);
if (ret) {
dev_err(dev, "Failed at AMX%d sw reset\n", dev->id);
return ret;
}
return (dcnt < 0) ? -ETIMEDOUT : 0;
}
static int tegra210_amx_runtime_suspend(struct device *dev) static int tegra210_amx_runtime_suspend(struct device *dev)
{ {
struct tegra210_amx *amx = dev_get_drvdata(dev); struct tegra210_amx *amx = dev_get_drvdata(dev);
@@ -329,7 +386,14 @@ int tegra210_amx_set_channel_map(struct snd_soc_dai *dai,
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai); struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
unsigned int byte_mask1 = 0, byte_mask2 = 0; unsigned int byte_mask1 = 0, byte_mask2 = 0;
unsigned int in_stream_idx, in_ch_idx, in_byte_idx; unsigned int in_stream_idx, in_ch_idx, in_byte_idx;
int i; int i, ret = 0;
/* HW needs sw reset to make sure previous transaction be clean */
tegra210_amx_sw_reset(amx, 0xffff);
if (ret) {
dev_err(dev, "Failed at AMX sw reset\n");
return ret;
}
if ((tx_num < 1) || (tx_num > 64)) { if ((tx_num < 1) || (tx_num > 64)) {
dev_err(dev, "Doesn't support %d tx_num, need to be 1 to 64\n", dev_err(dev, "Doesn't support %d tx_num, need to be 1 to 64\n",
@@ -343,7 +407,7 @@ int tegra210_amx_set_channel_map(struct snd_soc_dai *dai,
} }
tegra210_amx_set_master_stream(amx, 0, tegra210_amx_set_master_stream(amx, 0,
TEGRA210_AMX_WAIT_ON_ALL); TEGRA210_AMX_WAIT_ON_ANY);
memset(amx->map, 0, sizeof(amx->map)); memset(amx->map, 0, sizeof(amx->map));
@@ -438,7 +502,9 @@ static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = {
SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0), SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0), SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0),
SND_SOC_DAPM_AIF_IN("IN4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0), SND_SOC_DAPM_AIF_IN("IN4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0),
SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, TEGRA210_AMX_ENABLE, 0, 0), SND_SOC_DAPM_AIF_OUT_E("OUT", NULL, 0, TEGRA210_AMX_ENABLE,
TEGRA210_AMX_ENABLE_SHIFT, 0,
tegra210_amx_stop, SND_SOC_DAPM_POST_PMD),
}; };
static const struct snd_soc_dapm_route tegra210_amx_routes[] = { static const struct snd_soc_dapm_route tegra210_amx_routes[] = {
@@ -528,6 +594,29 @@ static bool tegra210_amx_rd_reg(struct device *dev,
}; };
} }
static bool tegra210_amx_volatile_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_AMX_AXBAR_RX_STATUS:
case TEGRA210_AMX_AXBAR_RX_INT_STATUS:
case TEGRA210_AMX_AXBAR_RX_INT_SET:
case TEGRA210_AMX_AXBAR_TX_STATUS:
case TEGRA210_AMX_AXBAR_TX_INT_STATUS:
case TEGRA210_AMX_AXBAR_TX_INT_SET:
case TEGRA210_AMX_SOFT_RESET:
case TEGRA210_AMX_STATUS:
case TEGRA210_AMX_INT_STATUS:
case TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL:
case TEGRA210_AMX_AHUBRAMCTL_AMX_DATA:
return true;
default:
break;
};
return false;
}
static const struct regmap_config tegra210_amx_regmap_config = { static const struct regmap_config tegra210_amx_regmap_config = {
.reg_bits = 32, .reg_bits = 32,
.reg_stride = 4, .reg_stride = 4,
@@ -535,6 +624,7 @@ static const struct regmap_config tegra210_amx_regmap_config = {
.max_register = TEGRA210_AMX_AHUBRAMCTL_AMX_DATA, .max_register = TEGRA210_AMX_AHUBRAMCTL_AMX_DATA,
.writeable_reg = tegra210_amx_wr_reg, .writeable_reg = tegra210_amx_wr_reg,
.readable_reg = tegra210_amx_rd_reg, .readable_reg = tegra210_amx_rd_reg,
.volatile_reg = tegra210_amx_volatile_reg,
.cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE,
}; };

View File

@@ -15,6 +15,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
@@ -96,6 +98,112 @@ static int tegra210_i2s_set_clock_rate(struct device *dev, int clock_rate)
return ret; return ret;
} }
static int tegra210_i2s_sw_reset(struct tegra210_i2s *i2s,
int direction, int timeout)
{
unsigned int sw_reset_reg, sw_reset_mask, sw_reset_en, sw_reset_default;
unsigned int tx_cif_ctrl, rx_cif_ctrl, tx_ctrl, rx_ctrl, ctrl, val;
int wait = timeout;
regmap_read(i2s->regmap, TEGRA210_I2S_AXBAR_TX_CIF_CTRL, &tx_cif_ctrl);
regmap_read(i2s->regmap, TEGRA210_I2S_AXBAR_RX_CIF_CTRL, &rx_cif_ctrl);
regmap_read(i2s->regmap, TEGRA210_I2S_AXBAR_TX_CTRL, &tx_ctrl);
regmap_read(i2s->regmap, TEGRA210_I2S_AXBAR_RX_CTRL, &rx_ctrl);
regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &ctrl);
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
sw_reset_reg = TEGRA210_I2S_AXBAR_TX_SOFT_RESET;
sw_reset_mask = TEGRA210_I2S_AXBAR_TX_SOFT_RESET_MASK;
sw_reset_en = TEGRA210_I2S_AXBAR_TX_SOFT_RESET_EN;
sw_reset_default = TEGRA210_I2S_AXBAR_TX_SOFT_RESET_DEFAULT;
} else {
sw_reset_reg = TEGRA210_I2S_AXBAR_RX_SOFT_RESET;
sw_reset_mask = TEGRA210_I2S_AXBAR_RX_SOFT_RESET_MASK;
sw_reset_en = TEGRA210_I2S_AXBAR_RX_SOFT_RESET_EN;
sw_reset_default = TEGRA210_I2S_AXBAR_RX_SOFT_RESET_DEFAULT;
}
regmap_update_bits(i2s->regmap, sw_reset_reg, sw_reset_mask, sw_reset_en);
do {
regmap_read(i2s->regmap, sw_reset_reg, &val);
wait--;
if (!wait)
return -EINVAL;
} while (val & sw_reset_mask);
regmap_update_bits(i2s->regmap, sw_reset_reg, sw_reset_mask, sw_reset_default);
regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_TX_CIF_CTRL, tx_cif_ctrl);
regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_RX_CIF_CTRL, rx_cif_ctrl);
regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_TX_CTRL, tx_ctrl);
regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_RX_CTRL, rx_ctrl);
regmap_write(i2s->regmap, TEGRA210_I2S_CTRL, ctrl);
return 0;
}
static int tegra210_i2s_get_status(struct tegra210_i2s *i2s,
int direction)
{
unsigned int status_reg, val;
status_reg = (direction == SNDRV_PCM_STREAM_PLAYBACK) ?
TEGRA210_I2S_AXBAR_TX_STATUS :
TEGRA210_I2S_AXBAR_RX_STATUS;
regmap_read(i2s->regmap, status_reg, &val);
val = val & 0x00000001;
return val;
}
static int tegra210_i2s_rx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct device *dev = codec->dev;
struct tegra210_i2s *i2s = dev_get_drvdata(dev);
int dcnt = 10, ret;
/* wait until I2S RX status is disabled;
ADMAIF is first disabled followed by I2S */
while (tegra210_i2s_get_status(i2s, SNDRV_PCM_STREAM_CAPTURE) &&
dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_i2s_sw_reset(i2s, SNDRV_PCM_STREAM_CAPTURE, 0xffff);
if (ret) {
dev_err(dev, "Failed at I2S%d_RX sw reset\n", dev->id);
return ret;
}
return 0;
}
static int tegra210_i2s_tx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct device *dev = codec->dev;
struct tegra210_i2s *i2s = dev_get_drvdata(dev);
int dcnt = 10, ret;
/* wait until I2S TX status is disabled;
ADMAIF is first disabled followed by I2S */
while (tegra210_i2s_get_status(i2s, SNDRV_PCM_STREAM_PLAYBACK) &&
dcnt--)
udelay(100);
/* HW needs sw reset to make sure previous transaction be clean */
ret = tegra210_i2s_sw_reset(i2s, SNDRV_PCM_STREAM_PLAYBACK, 0xffff);
if (ret) {
dev_err(dev, "Failed at I2S%d_TX sw reset\n", dev->id);
return ret;
}
return 0;
}
static int tegra210_i2s_runtime_suspend(struct device *dev) static int tegra210_i2s_runtime_suspend(struct device *dev)
{ {
struct tegra210_i2s *i2s = dev_get_drvdata(dev); struct tegra210_i2s *i2s = dev_get_drvdata(dev);
@@ -489,10 +597,12 @@ static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = {
0, 0), 0, 0),
SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM, SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM,
0, 0), 0, 0),
SND_SOC_DAPM_AIF_IN("DAP RX", NULL, 0, TEGRA210_I2S_AXBAR_TX_ENABLE, SND_SOC_DAPM_AIF_IN_E("DAP RX", NULL, 0, TEGRA210_I2S_AXBAR_TX_ENABLE,
TEGRA210_I2S_AXBAR_TX_EN_SHIFT, 0), TEGRA210_I2S_AXBAR_TX_EN_SHIFT, 0,
SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA210_I2S_AXBAR_RX_ENABLE, tegra210_i2s_rx_stop, SND_SOC_DAPM_POST_PMD),
TEGRA210_I2S_AXBAR_RX_EN_SHIFT, 0), SND_SOC_DAPM_AIF_OUT_E("DAP TX", NULL, 0, TEGRA210_I2S_AXBAR_RX_ENABLE,
TEGRA210_I2S_AXBAR_RX_EN_SHIFT, 0,
tegra210_i2s_tx_stop, SND_SOC_DAPM_POST_PMD),
}; };
static const struct snd_soc_dapm_route tegra210_i2s_routes[] = { static const struct snd_soc_dapm_route tegra210_i2s_routes[] = {