ASoC: tegra-alt: use regmap macros for timeout

Following print is seen while using regmap_read_poll_timeout() macro
in snd_soc_dai_ops trigger() callback in ADMAIF driver,
 *** BUG: scheduling while atomic ***
and kernel panic happens there after. This happens because above macro
is not atomic safe and trigger() is called in atomic context.

Earlier this addressed by using readx_poll_timeout_atomic() macro.
Though this works fine driver needs to keep a reference to base address.
To avoid this new macro was pushed and is accepted in upstream and
following is the commit - https://lkml.org/lkml/2020/1/9/985

This patch makes use of regmap macros where there is a need of timeout
functionality. Following is the guideline that can be followed.
 * use regmap_read_poll_timeout_atomic() where the caller is in atomic
   context.
   ex: trigger() etc.,
 * use regmap_read_poll_timeout() when the caller is known to be running
   in non-atomic context.
   ex: hw_param(), prepare() etc.,

Based on this audio drivers are updated. ASRC and ARAD drivers will be
taken up in a separate change.

Bug 200566596

Change-Id: Iea88adc60de5919e456a36a30152212652a8ecd3
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2274874
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Sharad Gupta <sharadg@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Sameer Pujar
2020-01-07 14:46:18 +05:30
parent 27008c3e63
commit f44cfefe5d
9 changed files with 152 additions and 227 deletions

View File

@@ -209,7 +209,6 @@ struct tegra210_i2s {
bool loopback; bool loopback;
unsigned int format; unsigned int format;
unsigned int rx_fifo_th; /* should be programmed interms of frames */ unsigned int rx_fifo_th; /* should be programmed interms of frames */
void __iomem *base_addr;
}; };
#endif #endif

View File

@@ -16,10 +16,8 @@
* 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/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@@ -431,8 +429,8 @@ static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction)
regmap_update_bits(admaif->regmap, enable_reg, mask, ~enable); regmap_update_bits(admaif->regmap, enable_reg, mask, ~enable);
/* wait until ADMAIF TX/RX status is disabled */ /* wait until ADMAIF TX/RX status is disabled */
ret = readl_poll_timeout_atomic(REG_IOVA(status_reg), val, ret = regmap_read_poll_timeout_atomic(admaif->regmap, status_reg, val,
!(val & enable), 10, 10000); !(val & enable), 10, 10000);
/* Timeout may be hit if sink gets closed/blocked ahead of source */ /* Timeout may be hit if sink gets closed/blocked ahead of source */
if (ret < 0) if (ret < 0)
@@ -443,9 +441,9 @@ static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction)
regmap_update_bits(admaif->regmap, reset_reg, SW_RESET_MASK, SW_RESET); regmap_update_bits(admaif->regmap, reset_reg, SW_RESET_MASK, SW_RESET);
/* wait till SW reset is complete */ /* wait till SW reset is complete */
ret = readl_poll_timeout_atomic(REG_IOVA(reset_reg), val, ret = regmap_read_poll_timeout_atomic(admaif->regmap, reset_reg, val,
!(val & SW_RESET_MASK & SW_RESET), !(val & SW_RESET_MASK & SW_RESET),
10, 10000); 10, 10000);
if (ret < 0) { if (ret < 0) {
dev_err(dai->dev, "timeout: SW reset failed for ADMAIF%d_%s\n", dev_err(dai->dev, "timeout: SW reset failed for ADMAIF%d_%s\n",
dai->id + 1, dir_name); dai->id + 1, dir_name);

View File

@@ -16,7 +16,6 @@
* 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>
@@ -155,68 +154,47 @@ 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, static int tegra210_adx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct device *dev = codec->dev; struct device *dev = codec->dev;
struct tegra210_adx *adx = dev_get_drvdata(dev); struct tegra210_adx *adx = dev_get_drvdata(dev);
int dcnt = 10, ret = 0; unsigned int val;
int ret;
/* wait until ADX status is disabled */ /* ensure if ADX status is disabled */
while (tegra210_adx_get_status(adx) && dcnt--) ret = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS,
udelay(100); val, !(val & 0x1), 10, 10000);
if (ret < 0) {
/* HW needs sw reset to make sure previous transaction be clean */ dev_err(dev, "failed to stop ADX, err = %d\n", ret);
ret = tegra210_adx_sw_reset(adx, 0xffff);
if (ret) {
dev_err(dev, "Failed at ADX%d sw reset\n", dev->id);
return ret; return ret;
} }
return (dcnt < 0) ? -ETIMEDOUT : 0; /* SW reset */
regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_ADX_SOFT_RESET_SOFT_EN);
ret = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET,
val, !(val & 0x1), 10, 10000);
if (ret < 0) {
dev_err(dev, "failed to reset ADX, err = %d\n", ret);
return ret;
}
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 unsigned int __maybe_unused tegra210_adx_read_map_ram( static unsigned int __maybe_unused
struct tegra210_adx *adx, tegra210_adx_read_map_ram(struct tegra210_adx *adx, unsigned int addr)
unsigned int addr)
{ {
unsigned int val, wait; unsigned int val;
wait = 0xffff; int ret;
regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL,
(addr << TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RAM_ADDR_SHIFT)); (addr << TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RAM_ADDR_SHIFT));
@@ -228,12 +206,11 @@ static unsigned int __maybe_unused tegra210_adx_read_map_ram(
val &= ~(TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RW_WRITE); val &= ~(TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RW_WRITE);
regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, val); regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, val);
do { ret = regmap_read_poll_timeout(adx->regmap,
regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, &val); TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL,
wait--; val, !(val & 0x80000000), 10, 10000);
if (!wait) if (ret < 0)
return -EINVAL; return ret;
} while (val & 0x80000000);
regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_DATA, &val); regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_DATA, &val);

View File

@@ -1,7 +1,7 @@
/* /*
* tegra210_afc_alt.c - Tegra210 AFC driver * tegra210_afc_alt.c - Tegra210 AFC driver
* *
* Copyright (c) 2014-2019 NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020 NVIDIA CORPORATION. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@@ -15,7 +15,6 @@
* 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>

View File

@@ -16,7 +16,6 @@
* 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>
@@ -179,60 +178,40 @@ 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, static int tegra210_amx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct device *dev = codec->dev; struct device *dev = codec->dev;
struct tegra210_amx *amx = dev_get_drvdata(dev); struct tegra210_amx *amx = dev_get_drvdata(dev);
int dcnt = 10, ret = 0; unsigned int val;
int ret;
/* wait until AMX status is disabled */ /* Ensure if AMX is disabled */
while (tegra210_amx_get_status(amx) && dcnt--) ret = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_STATUS, val,
udelay(100); !(val & 0x1), 10, 10000);
if (ret < 0) {
/* HW needs sw reset to make sure previous transaction be clean */ dev_err(dev, "failed to stop AMX, err = %d\n", ret);
ret = tegra210_amx_sw_reset(amx, 0xffff);
if (ret) {
dev_err(dev, "Failed at AMX%d sw reset\n", dev->id);
return ret; return ret;
} }
return (dcnt < 0) ? -ETIMEDOUT : 0; /* SW reset */
regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_AMX_SOFT_RESET_SOFT_EN);
ret = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_SOFT_RESET,
val, !(val & 0x1), 10, 10000);
if (ret < 0) {
dev_err(dev, "failed to reset AMX, err = %d\n", ret);
return ret;
}
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_runtime_suspend(struct device *dev) static int tegra210_amx_runtime_suspend(struct device *dev)
@@ -245,12 +224,11 @@ static int tegra210_amx_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static unsigned int __maybe_unused tegra210_amx_read_map_ram( static unsigned int __maybe_unused
struct tegra210_amx *amx, tegra210_amx_read_map_ram(struct tegra210_amx *amx, unsigned int addr)
unsigned int addr)
{ {
unsigned int val, wait; unsigned int val;
wait = 0xffff; int ret;
regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL,
(addr << TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RAM_ADDR_SHIFT)); (addr << TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RAM_ADDR_SHIFT));
@@ -262,13 +240,11 @@ static unsigned int __maybe_unused tegra210_amx_read_map_ram(
val &= ~(TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RW_WRITE); val &= ~(TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RW_WRITE);
regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, val); regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, val);
do { ret = regmap_read_poll_timeout(amx->regmap,
regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL,
TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, &val); val, !(val & 0x80000000), 10, 10000);
wait--; if (ret < 0)
if (!wait) return ret;
return -EINVAL;
} while (val & 0x80000000);
regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_DATA, &val); regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_DATA, &val);

View File

@@ -16,11 +16,9 @@
* 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>
#include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@@ -33,7 +31,6 @@
#include <linux/pinctrl/pinconf-tegra.h> #include <linux/pinctrl/pinconf-tegra.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/delay.h>
#include <linux/tegra-powergate.h> #include <linux/tegra-powergate.h>
#include <soc/tegra/chip-id.h> #include <soc/tegra/chip-id.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
@@ -43,8 +40,6 @@
#define DRV_NAME "tegra210-i2s" #define DRV_NAME "tegra210-i2s"
#define REG_IOVA(reg) (i2s->base_addr + (reg))
static const struct reg_default tegra210_i2s_reg_defaults[] = { static const struct reg_default tegra210_i2s_reg_defaults[] = {
{ TEGRA210_I2S_AXBAR_RX_INT_MASK, 0x00000003}, { TEGRA210_I2S_AXBAR_RX_INT_MASK, 0x00000003},
{ TEGRA210_I2S_AXBAR_RX_CIF_CTRL, 0x00007700}, { TEGRA210_I2S_AXBAR_RX_CIF_CTRL, 0x00007700},
@@ -144,9 +139,9 @@ static int tegra210_i2s_sw_reset(struct snd_soc_codec *codec, bool is_playback)
/* SW reset */ /* SW reset */
regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en); regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en);
ret = readl_poll_timeout_atomic(REG_IOVA(reset_reg), val, ret = regmap_read_poll_timeout(i2s->regmap, reset_reg, val,
!(val & reset_mask & reset_en), !(val & reset_mask & reset_en),
10, 10000); 10, 10000);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "timeout: failed to reset I2S for %s\n", dev_err(dev, "timeout: failed to reset I2S for %s\n",
is_playback ? "playback" : "capture"); is_playback ? "playback" : "capture");
@@ -185,7 +180,7 @@ static int tegra210_i2s_init(struct snd_soc_dapm_widget *w,
} }
/* ensure I2S is in disabled state before new session */ /* ensure I2S is in disabled state before new session */
ret = readl_poll_timeout_atomic(REG_IOVA(status_reg), val, ret = regmap_read_poll_timeout(i2s->regmap, status_reg, val,
!(val & TEGRA210_I2S_EN_MASK & TEGRA210_I2S_EN), !(val & TEGRA210_I2S_EN_MASK & TEGRA210_I2S_EN),
10, 10000); 10, 10000);
if (ret < 0) { if (ret < 0) {
@@ -1100,8 +1095,6 @@ static int tegra210_i2s_platform_probe(struct platform_device *pdev)
if (IS_ERR(regs)) if (IS_ERR(regs))
return PTR_ERR(regs); return PTR_ERR(regs);
i2s->base_addr = regs;
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&tegra210_i2s_regmap_config); &tegra210_i2s_regmap_config);
if (IS_ERR(i2s->regmap)) { if (IS_ERR(i2s->regmap)) {

View File

@@ -95,20 +95,18 @@ static int tegra210_mixer_runtime_resume(struct device *dev)
} }
static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer, static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
unsigned int addr, unsigned int addr,
unsigned int val) unsigned int coef)
{ {
unsigned int reg, value, wait = 0xffff; unsigned int reg, val;
int ret;
/* check if busy */ /* check if busy */
do { ret = regmap_read_poll_timeout(mixer->regmap,
regmap_read(mixer->regmap, TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL,
TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL, &value); val, !(val & 0x80000000), 10, 10000);
wait--; if (ret < 0)
if (!wait) return ret;
return -EINVAL;
} while (value & 0x80000000);
value = 0;
reg = (addr << TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) & reg = (addr << TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) &
TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_MASK; TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_MASK;
@@ -117,9 +115,11 @@ static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
reg |= TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_SEQ_ACCESS_EN; reg |= TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_SEQ_ACCESS_EN;
regmap_write(mixer->regmap, regmap_write(mixer->regmap,
TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL, reg); TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL,
reg);
regmap_write(mixer->regmap, regmap_write(mixer->regmap,
TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA, val); TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA,
coef);
return 0; return 0;
} }

View File

@@ -28,7 +28,6 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/delay.h>
#include "tegra210_xbar_alt.h" #include "tegra210_xbar_alt.h"
#include "tegra210_mvc_alt.h" #include "tegra210_mvc_alt.h"
@@ -75,51 +74,27 @@ static int tegra210_mvc_runtime_resume(struct device *dev)
return 0; return 0;
} }
static int tegra210_mvc_soft_reset(struct tegra210_mvc *mvc)
{
int value;
int dcnt = 10;
/* issue soft reset */
regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
/* wait for soft reset bit to clear */
do {
udelay(10);
regmap_read(mvc->regmap, TEGRA210_MVC_SOFT_RESET, &value);
dcnt--;
if (dcnt < 0)
return -EINVAL;
} while (value);
return 0;
}
static int tegra210_mvc_write_ram(struct tegra210_mvc *mvc, static int tegra210_mvc_write_ram(struct tegra210_mvc *mvc,
unsigned int addr, unsigned int addr, unsigned int coef)
unsigned int val)
{ {
unsigned int reg, value, wait = 0xffff; unsigned int reg, val;
int ret;
/* check if busy */ ret = regmap_read_poll_timeout(mvc->regmap,
do { TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL,
regmap_read(mvc->regmap, val, !(val & 0x80000000), 10, 10000);
TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL, if (ret < 0)
&value); return ret;
wait--;
if (!wait)
return -EINVAL;
} while (value & 0x80000000);
value = 0;
reg = (addr << TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) & reg = (addr << TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) &
TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_MASK; TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_MASK;
reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN; reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN;
reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_WRITE; reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_WRITE;
reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN; reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN;
regmap_write(mvc->regmap, regmap_write(mvc->regmap, TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL, reg);
TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL, reg); regmap_write(mvc->regmap, TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_DATA,
regmap_write(mvc->regmap, coef);
TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_DATA, val);
return 0; return 0;
} }
@@ -161,21 +136,18 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tegra210_mvc *mvc = snd_soc_codec_get_drvdata(codec); struct tegra210_mvc *mvc = snd_soc_codec_get_drvdata(codec);
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
unsigned int value, wait = 0xffff; unsigned int value;
int ret = 0; int ret = 0;
s32 val; s32 val;
pm_runtime_get_sync(codec->dev); pm_runtime_get_sync(codec->dev);
/* check if VOLUME_SWITCH is triggered*/
do { /* check if VOLUME_SWITCH is triggered */
regmap_read(mvc->regmap, ret = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_SWITCH, &value); value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
wait--; 10, 10000);
if (!wait) { if (ret < 0)
ret = -EINVAL; goto end;
goto end;
}
} while (value & TEGRA210_MVC_VOLUME_SWITCH_MASK);
if (reg == TEGRA210_MVC_TARGET_VOL) { if (reg == TEGRA210_MVC_TARGET_VOL) {
/* Volume control read from mixer ctl is with */ /* Volume control read from mixer ctl is with */
@@ -357,11 +329,15 @@ static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
{ {
struct device *dev = dai->dev; struct device *dev = dai->dev;
struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai); struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
int i, ret; int i, ret, val;
ret = tegra210_mvc_soft_reset(mvc); /* SW reset */
regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
ret = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET,
val, !val, 10, 10000);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "SOFT_RESET error: %d\n", ret); dev_err(dev, "SW reset failed, err = %d\n", ret);
return ret; return ret;
} }
@@ -396,8 +372,14 @@ static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL, mvc->volume); regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL, mvc->volume);
/* program the poly coefficients */ /* program the poly coefficients */
for (i = 0; i < 9; i++) for (i = 0; i < 9; i++) {
tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]); ret = tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]);
if (ret < 0) {
dev_err(dai->dev, "failed to write coefs, err = %d\n",
ret);
return ret;
}
}
/* program poly_n1, poly_n2, duration */ /* program poly_n1, poly_n2, duration */
regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, mvc->poly_n1); regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, mvc->poly_n1);

View File

@@ -28,7 +28,6 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/delay.h>
#include "tegra210_xbar_alt.h" #include "tegra210_xbar_alt.h"
#include "tegra210_sfc_alt.h" #include "tegra210_sfc_alt.h"
@@ -3015,20 +3014,19 @@ static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc,
static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc) static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc)
{ {
u32 val; u32 val;
int cnt = 10; int ret;
int ret = 0;
regmap_update_bits(sfc->regmap, /* SW reset */
TEGRA210_SFC_SOFT_RESET, regmap_update_bits(sfc->regmap, TEGRA210_SFC_SOFT_RESET,
TEGRA210_SFC_SOFT_RESET_EN, TEGRA210_SFC_SOFT_RESET_EN, 1);
1);
do { ret = regmap_read_poll_timeout(sfc->regmap, TEGRA210_SFC_SOFT_RESET,
udelay(100); val, !(val & TEGRA210_SFC_SOFT_RESET_EN),
regmap_read(sfc->regmap, TEGRA210_SFC_SOFT_RESET, &val); 10, 10000);
} while ((val & TEGRA210_SFC_SOFT_RESET_EN) && cnt--); if (ret < 0)
if (!cnt) return ret;
ret = -ETIMEDOUT;
return ret; return 0;
} }
static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream, static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
@@ -3045,8 +3043,10 @@ static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
0); 0);
ret = tegra210_sfc_soft_reset(sfc); ret = tegra210_sfc_soft_reset(sfc);
if (ret) { if (ret < 0) {
dev_err(dev, "SOFT_RESET error: %d\n", ret); dev_err(dev, "failed to reset SFC in %s, err = %d\n",
__func__, ret);
return ret; return ret;
} }
@@ -3200,27 +3200,28 @@ static int tegra210_sfc_init_put(struct snd_kcontrol *kcontrol,
if (is_enabled) { if (is_enabled) {
u32 val; u32 val;
int cnt = 100;
regmap_write(sfc->regmap, TEGRA210_SFC_ENABLE, 0); regmap_write(sfc->regmap, TEGRA210_SFC_ENABLE, 0);
regmap_read(sfc->regmap, TEGRA210_SFC_STATUS, &val); ret = regmap_read_poll_timeout(sfc->regmap,
while ((val & 1) && cnt--) { TEGRA210_SFC_STATUS, val,
udelay(100); !(val & 0x1), 10, 10000);
regmap_read(sfc->regmap, TEGRA210_SFC_STATUS, &val); if (ret < 0) {
dev_err(codec->dev,
"failed to disable SFC, err = %d\n", ret);
return ret;
} }
if (!cnt)
dev_warn(codec->dev, "SFC disable timeout\n");
regmap_update_bits(sfc->regmap, regmap_update_bits(sfc->regmap,
TEGRA210_SFC_COEF_RAM, TEGRA210_SFC_COEF_RAM,
TEGRA210_SFC_COEF_RAM_COEF_RAM_EN, TEGRA210_SFC_COEF_RAM_COEF_RAM_EN,
0); 0);
ret = tegra210_sfc_soft_reset(sfc); ret = tegra210_sfc_soft_reset(sfc);
if (ret) { if (ret < 0) {
dev_err(codec->dev, "SOFT_RESET error: %d\n", ret); dev_err(codec->dev,
"failed to reset SFC in %s, err = %d\n",
__func__, ret);
goto exit; goto exit;
} }