mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
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:
committed by
Sameer Pujar
parent
8f48438124
commit
684a5ee845
@@ -15,6 +15,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
@@ -154,6 +156,25 @@ static int tegra210_admaif_sw_reset(struct snd_soc_dai *dai,
|
||||
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)
|
||||
{
|
||||
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_xbar_cif_conf cif_conf;
|
||||
unsigned int reg, fifo_ctrl, fifo_size;
|
||||
int valid_bit, ret;
|
||||
int valid_bit;
|
||||
|
||||
cif_conf.audio_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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int reg;
|
||||
int dcnt = 10, ret;
|
||||
|
||||
tegra210_admaif_global_enable(admaif, 0);
|
||||
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,
|
||||
TEGRA210_ADMAIF_XBAR_TX_ENABLE_MASK,
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int reg;
|
||||
int dcnt = 10, ret;
|
||||
|
||||
tegra210_admaif_global_enable(admaif, 0);
|
||||
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,
|
||||
TEGRA210_ADMAIF_XBAR_RX_ENABLE_MASK,
|
||||
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,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.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]);
|
||||
}
|
||||
|
||||
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
|
||||
static unsigned int tegra210_adx_read_map_ram(struct tegra210_adx *adx,
|
||||
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);
|
||||
unsigned int byte_mask1 = 0, byte_mask2 = 0;
|
||||
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)) {
|
||||
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[] = {
|
||||
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("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 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_SOFT_RESET:
|
||||
case TEGRA210_ADX_CG:
|
||||
case TEGRA210_ADX_STATUS:
|
||||
case TEGRA210_ADX_INT_STATUS:
|
||||
case TEGRA210_ADX_CTRL:
|
||||
case TEGRA210_ADX_IN_BYTE_EN0:
|
||||
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 = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
@@ -500,6 +591,7 @@ static const struct regmap_config tegra210_adx_regmap_config = {
|
||||
.max_register = TEGRA210_ADX_AHUBRAMCTL_ADX_DATA,
|
||||
.writeable_reg = tegra210_adx_wr_reg,
|
||||
.readable_reg = tegra210_adx_rd_reg,
|
||||
.volatile_reg = tegra210_adx_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.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]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
unsigned int byte_mask1 = 0, byte_mask2 = 0;
|
||||
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)) {
|
||||
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_WAIT_ON_ALL);
|
||||
TEGRA210_AMX_WAIT_ON_ANY);
|
||||
|
||||
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("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_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[] = {
|
||||
@@ -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 = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
@@ -535,6 +624,7 @@ static const struct regmap_config tegra210_amx_regmap_config = {
|
||||
.max_register = TEGRA210_AMX_AHUBRAMCTL_AMX_DATA,
|
||||
.writeable_reg = tegra210_amx_wr_reg,
|
||||
.readable_reg = tegra210_amx_rd_reg,
|
||||
.volatile_reg = tegra210_amx_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
@@ -96,6 +98,112 @@ static int tegra210_i2s_set_clock_rate(struct device *dev, int clock_rate)
|
||||
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)
|
||||
{
|
||||
struct tegra210_i2s *i2s = dev_get_drvdata(dev);
|
||||
@@ -489,10 +597,12 @@ static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = {
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM,
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_IN("DAP RX", NULL, 0, TEGRA210_I2S_AXBAR_TX_ENABLE,
|
||||
TEGRA210_I2S_AXBAR_TX_EN_SHIFT, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA210_I2S_AXBAR_RX_ENABLE,
|
||||
TEGRA210_I2S_AXBAR_RX_EN_SHIFT, 0),
|
||||
SND_SOC_DAPM_AIF_IN_E("DAP RX", NULL, 0, TEGRA210_I2S_AXBAR_TX_ENABLE,
|
||||
TEGRA210_I2S_AXBAR_TX_EN_SHIFT, 0,
|
||||
tegra210_i2s_rx_stop, SND_SOC_DAPM_POST_PMD),
|
||||
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[] = {
|
||||
|
||||
Reference in New Issue
Block a user