diff --git a/sound/soc/tegra-alt/tegra210_admaif_alt.c b/sound/soc/tegra-alt/tegra210_admaif_alt.c index 06a166a7..99ef4b5f 100644 --- a/sound/soc/tegra-alt/tegra210_admaif_alt.c +++ b/sound/soc/tegra-alt/tegra210_admaif_alt.c @@ -15,6 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + +#include #include #include #include @@ -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, diff --git a/sound/soc/tegra-alt/tegra210_adx_alt.c b/sound/soc/tegra-alt/tegra210_adx_alt.c index 3fff4bc6..aabdcb65 100644 --- a/sound/soc/tegra-alt/tegra210_adx_alt.c +++ b/sound/soc/tegra-alt/tegra210_adx_alt.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -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, }; diff --git a/sound/soc/tegra-alt/tegra210_amx_alt.c b/sound/soc/tegra-alt/tegra210_amx_alt.c index 80c167f7..f7da5c12 100644 --- a/sound/soc/tegra-alt/tegra210_amx_alt.c +++ b/sound/soc/tegra-alt/tegra210_amx_alt.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -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, }; diff --git a/sound/soc/tegra-alt/tegra210_i2s_alt.c b/sound/soc/tegra-alt/tegra210_i2s_alt.c index 9eb90a64..c4d32d26 100644 --- a/sound/soc/tegra-alt/tegra210_i2s_alt.c +++ b/sound/soc/tegra-alt/tegra210_i2s_alt.c @@ -15,6 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + +#include #include #include #include @@ -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[] = {