ASoC: tegra-alt: WAR for ARAD/ASRC

Issue is seen with ARAD/ASRC when it goes to
UNLOCK->LOCK.
- ASRC may not check for buffer thresholds once it
restarts processing after an UNLOCK->LOCK.
- ARAD will not get locked after an unlock

Bug 200118889

Change-Id: Ia16187df6955e79f93f3a7cb9061ea64a61c1d36
Signed-off-by: Dipesh Gandhi <dipeshg@nvidia.com>
Reviewed-on: http://git-master/r/764096
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Tested-by: Sumit Bhattacharya <sumitb@nvidia.com>
This commit is contained in:
Dipesh Gandhi
2015-06-30 15:05:12 +05:30
committed by Sameer Pujar
parent 29630055e7
commit 21c45bba08
3 changed files with 218 additions and 8 deletions

View File

@@ -49,3 +49,12 @@ config SND_SOC_TEGRA_T186REF_ALT
select SND_SOC_AD193X
help
Say Y or M here.
config SND_SOC_TEGRA186_ARAD_WAR
def_bool y
depends on SND_SOC_TEGRA186_ARAD_ALT
config SND_SOC_TEGRA186_ASRC_WAR
def_bool n
depends on SND_SOC_TEGRA186_ASRC_ALT
depends on SND_SOC_TEGRA186_ARAD_WAR

View File

@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/irqchip/tegra-agic.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -32,6 +33,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/of_device.h>
#include "tegra186_asrc_alt.h"
#include "tegra210_xbar_alt.h"
#include "tegra186_arad_alt.h"
@@ -57,7 +59,6 @@
{ ARAD_LANE_REG(TEGRA186_ARAD_LANE1_ERROR_UNLOCK_THRESHOLD, id), 0xa00000}, \
{ ARAD_LANE_REG(TEGRA186_ARAD_LANE1_RATIO_CALCULATOR_CONFIG, id), 0xf000006}, \
{ ARAD_LANE_REG(TEGRA186_ARAD_LANE1_CYA, id), 0x0}
static const struct reg_default tegra186_arad_reg_defaults[] = {
{ TEGRA186_ARAD_LANE_ENABLE, 0x0},
{ TEGRA186_ARAD_LANE_SOFT_RESET, 0x0},
@@ -77,7 +78,6 @@ static const struct reg_default tegra186_arad_reg_defaults[] = {
{ TEGRA186_ARAD_TX_CIF_CTRL, 0x115500},
};
static int tegra186_arad_runtime_suspend(struct device *dev)
{
struct tegra186_arad *arad = dev_get_drvdata(dev);
@@ -138,6 +138,17 @@ static int tegra186_arad_get_lane_lock_status(
return val;
}
static int tegra186_arad_get_lane_ratio_change_status(
struct tegra186_arad *arad, unsigned int lane_id)
{
unsigned int val;
regmap_read(arad->regmap,
TEGRA186_ARAD_LANE_INT_STATUS, &val);
val = (val >> (16 + lane_id)) & 0x1;
return val;
}
static struct snd_soc_dai_ops tegra186_arad_out_dai_ops = {
};
@@ -290,6 +301,13 @@ static int tegra186_arad_put_enable_lane(struct snd_kcontrol *kcontrol,
arad_private->reg, 1<<arad_private->shift,
enable<<arad_private->shift);
#ifdef CONFIG_SND_SOC_TEGRA186_ARAD_WAR
regmap_update_bits(arad->regmap,
TEGRA186_ARAD_LANE_INT_MASK,
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT + lane_id),
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT + lane_id));
#endif
if (enable)
while (!tegra186_arad_get_lane_lock_status(arad, lane_id) &&
dcnt--)
@@ -648,6 +666,81 @@ static const struct of_device_id tegra186_arad_of_match[] = {
{},
};
#ifdef CONFIG_SND_SOC_TEGRA186_ARAD_WAR
static irqreturn_t tegra186_arad_interrupt_handler(int irq, void *data)
{
unsigned long flags;
int i = 0, inte = 0, frac = 0;
unsigned int status = 0, val = 0;
struct tegra186_arad *arad = dev_get_drvdata(
(struct device *) data);
spin_lock_irqsave(&arad->int_lock, flags);
/* WAR for Bug 200102368 */
regmap_read(arad->regmap,
TEGRA186_ARAD_LANE_INT_STATUS, &status);
for (i = 0; i < TEGRA186_ARAD_LANE_MAX; i++) {
if ((1 << i) & status) {
regmap_read(arad->regmap,
TEGRA186_ARAD_LANE_ENABLE, &val);
val |= 1<<i;
regmap_write(arad->regmap,
TEGRA186_ARAD_LANE_INT_CLEAR, 1<<i);
regmap_write(arad->regmap,
TEGRA186_ARAD_LANE_SOFT_RESET, 1<<i);
regmap_write(arad->regmap,
TEGRA186_ARAD_LANE_ENABLE, val);
#ifdef CONFIG_SND_SOC_TEGRA186_ASRC_WAR
tegra186_asrc_event(i, STREAM_DISABLE, 0);
#endif
/* In case ratio change is masked becasue of
previous locking, unmask it*/
regmap_update_bits(arad->regmap,
TEGRA186_ARAD_LANE_INT_MASK,
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT
+ i), 0);
val = 0;
} /*
Its a case where interrupt is because of
ratio change, to offload unecessary interrupt
handling in locked state mask ratio change
interrupt */
else if (tegra186_arad_get_lane_lock_status(arad, i) &&
tegra186_arad_get_lane_ratio_change_status(arad, i)) {
regmap_update_bits(arad->regmap,
TEGRA186_ARAD_LANE_INT_MASK,
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT + i),
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT+i));
regmap_write(arad->regmap,
TEGRA186_ARAD_LANE_INT_CLEAR,
1<<(TEGRA186_ARAD_LANE_INT_RATIO_CHANGE_SHIFT+i));
#ifdef CONFIG_SND_SOC_TEGRA186_ASRC_WAR
regmap_read(arad->regmap, ARAD_LANE_REG(
TEGRA186_ARAD_LANE1_RATIO_INTEGER_PART,
i), &inte);
regmap_read(arad->regmap, ARAD_LANE_REG(
TEGRA186_ARAD_LANE1_RATIO_FRACTIONAL_PART,
i), &frac);
/* source SW:1 and ARAD:0 */
tegra186_asrc_set_source(i, 1);
tegra186_update_asrc_ratio(i, inte, frac);
tegra186_asrc_set_source(i, 0);
tegra186_asrc_event(i, STREAM_ENABLE, 1);
#endif
}
}
spin_unlock_irqrestore(&arad->int_lock, flags);
return IRQ_HANDLED;
}
#endif
static int tegra186_arad_platform_probe(struct platform_device *pdev)
{
struct tegra186_arad *arad;
@@ -723,6 +816,16 @@ static int tegra186_arad_platform_probe(struct platform_device *pdev)
goto err_pm_disable;
}
#ifdef CONFIG_SND_SOC_TEGRA186_ARAD_WAR
arad->irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev,
tegra_agic_irq_get_virq(arad->irq),
tegra186_arad_interrupt_handler,
0, pdev->name, &pdev->dev);
if (ret)
dev_err(&pdev->dev, "Could not register ARAD INTERRUPT\n");
spin_lock_init(&arad->int_lock);
#endif
ret = snd_soc_register_codec(&pdev->dev, &tegra186_arad_codec,
tegra186_arad_dais,
ARRAY_SIZE(tegra186_arad_dais));

View File

@@ -60,6 +60,14 @@
{ ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG, id), 0x64}, \
{ ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR, id), 0x4b0}, \
{ ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG, id), 0x64}
static struct device *asrc_dev;
static struct asrc_task_desc {
int stream_id;
enum task_event event;
int action;
struct list_head node;
};
static const struct reg_default tegra186_asrc_reg_defaults[] = {
ASRC_STREAM_REG_DEFAULTS(0),
@@ -90,6 +98,35 @@ static const struct reg_default tegra186_asrc_reg_defaults[] = {
{ TEGRA186_ASRC_CYA, 0x0},
};
static int tegra186_asrc_get_stream_enable_status(struct tegra186_asrc *asrc,
unsigned int lane_id)
{
int val;
regmap_read(asrc->regmap,
ASRC_STREAM_REG(
TEGRA186_ASRC_STREAM1_STATUS, lane_id),
&val);
return val & 0x01;
}
int tegra186_asrc_event(int id, enum task_event event, int action)
{
struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev);
struct asrc_task_desc *task = kzalloc(sizeof(*task), GFP_ATOMIC);
task->stream_id = id;
task->event = event;
task->action = action;
list_add_tail(&task->node, &asrc->task_desc);
tasklet_schedule(&asrc->tasklet);
return 0;
}
static int tegra186_asrc_get_ratio_lock_status(struct tegra186_asrc *asrc,
unsigned int lane_id)
{
@@ -120,6 +157,33 @@ static void tegra186_asrc_set_ratio_lock_status(struct tegra186_asrc *asrc,
TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS, lane_id), 1);
}
int tegra186_asrc_set_source(int id, int source)
{
struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev);
regmap_update_bits(asrc->regmap,
ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, id),
TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK, (source & 1));
return 0;
}
int tegra186_asrc_update_ratio(int id, int inte, int frac)
{
struct tegra186_asrc *asrc = dev_get_drvdata(asrc_dev);
regmap_write(asrc->regmap, ASRC_STREAM_REG(
TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART, id),
inte);
regmap_write(asrc->regmap, ASRC_STREAM_REG(
TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART, id),
frac);
tegra186_asrc_set_ratio_lock_status(asrc, (unsigned int)id);
return 0;
}
static int tegra186_asrc_runtime_suspend(struct device *dev)
{
struct tegra186_asrc *asrc = dev_get_drvdata(dev);
@@ -149,8 +213,8 @@ static int tegra186_asrc_runtime_resume(struct device *dev)
if (asrc->lane[lane_id].ratio_source == RATIO_SW) {
regmap_write(asrc->regmap,
ASRC_STREAM_REG(
TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART,
lane_id),
TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART,
lane_id),
asrc->lane[lane_id].int_part);
regmap_write(asrc->regmap,
ASRC_STREAM_REG(
@@ -203,7 +267,6 @@ static int tegra186_asrc_set_audio_cif(struct tegra186_asrc *asrc,
cif_conf.client_channels = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
asrc->soc_data->set_audio_cif(asrc->regmap, reg, &cif_conf);
return 0;
@@ -221,7 +284,6 @@ static int tegra186_asrc_in_hw_params(struct snd_pcm_substream *substream,
regmap_write(asrc->regmap,
ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_THRESHOLD, dai->id),
0x00201002);
ret = tegra186_asrc_set_audio_cif(asrc, params,
ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_RX_CIF_CTRL, dai->id));
if (ret) {
@@ -763,6 +825,34 @@ static bool tegra186_asrc_volatile_reg(struct device *dev, unsigned int reg)
return false;
};
}
static void tegra_asrc_tasklet(unsigned long data)
{
struct tegra186_asrc *asrc =
(struct tegra186_asrc *)data;
struct asrc_task_desc *desc;
while (!list_empty(&asrc->task_desc)) {
int dcnt = 10;
desc = list_first_entry(&asrc->task_desc,
typeof(*desc), node);
/* Currently we have event for stream enable/disable
, so bypassing event check. In future if more events gets
added this should be handled in switch */
regmap_write(asrc->regmap, ASRC_STREAM_REG
(TEGRA186_ASRC_STREAM1_ENABLE, desc->stream_id),
desc->action);
if (!desc->action)
udelay(2000);
while ((tegra186_asrc_get_stream_enable_status(
asrc, desc->stream_id)
!= desc->action) && dcnt--)
udelay(100);
list_del(&desc->node);
kfree(desc);
}
}
static const struct regmap_config tegra186_asrc_regmap_config = {
.reg_bits = 32,
@@ -785,7 +875,6 @@ static const struct of_device_id tegra186_asrc_of_match[] = {
{ .compatible = "nvidia,tegra186-asrc", .data = &soc_data_tegra186 },
{},
};
static int tegra186_asrc_platform_probe(struct platform_device *pdev)
{
struct tegra186_asrc *asrc;
@@ -803,7 +892,7 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev)
goto err;
}
soc_data = (struct tegra186_asrc_soc_data *)match->data;
asrc_dev = &pdev->dev;
asrc = devm_kzalloc(&pdev->dev,
sizeof(struct tegra186_asrc), GFP_KERNEL);
if (!asrc) {
@@ -883,6 +972,11 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev)
ASRC_STREAM_REG(TEGRA186_ASRC_STREAM1_CONFIG, i), 1, 1);
}
INIT_LIST_HEAD(&asrc->task_desc);
tasklet_init(&asrc->tasklet, tegra_asrc_tasklet,
(unsigned long)asrc);
ret = snd_soc_register_codec(&pdev->dev, &tegra186_asrc_codec,
tegra186_asrc_dais,
ARRAY_SIZE(tegra186_asrc_dais));
@@ -904,6 +998,10 @@ err:
static int tegra186_asrc_platform_remove(struct platform_device *pdev)
{
struct tegra186_asrc *asrc =
dev_get_drvdata(&pdev->dev);
tasklet_kill(&asrc->tasklet);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);