From 21c45bba08a41506612e41f005fed6454fc134ae Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Tue, 30 Jun 2015 15:05:12 +0530 Subject: [PATCH] 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 Reviewed-on: http://git-master/r/764096 Reviewed-by: Sumit Bhattacharya Tested-by: Sumit Bhattacharya --- sound/soc/tegra-alt/Kconfig | 9 ++ sound/soc/tegra-alt/tegra186_arad_alt.c | 107 ++++++++++++++++++++++- sound/soc/tegra-alt/tegra186_asrc_alt.c | 110 ++++++++++++++++++++++-- 3 files changed, 218 insertions(+), 8 deletions(-) diff --git a/sound/soc/tegra-alt/Kconfig b/sound/soc/tegra-alt/Kconfig index a868a680..c8531156 100644 --- a/sound/soc/tegra-alt/Kconfig +++ b/sound/soc/tegra-alt/Kconfig @@ -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 diff --git a/sound/soc/tegra-alt/tegra186_arad_alt.c b/sound/soc/tegra-alt/tegra186_arad_alt.c index 99f08dc2..1326ac5e 100644 --- a/sound/soc/tegra-alt/tegra186_arad_alt.c +++ b/sound/soc/tegra-alt/tegra186_arad_alt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#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<shift, enable<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<regmap, + TEGRA186_ARAD_LANE_INT_CLEAR, 1<regmap, + TEGRA186_ARAD_LANE_SOFT_RESET, 1<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)); diff --git a/sound/soc/tegra-alt/tegra186_asrc_alt.c b/sound/soc/tegra-alt/tegra186_asrc_alt.c index 30138dc1..7f122a9b 100644 --- a/sound/soc/tegra-alt/tegra186_asrc_alt.c +++ b/sound/soc/tegra-alt/tegra186_asrc_alt.c @@ -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);