summary refs log tree commit diff
diff options
context:
space:
mode:
authorJerome Brunet <jbrunet@baylibre.com>2019-07-03 14:07:49 +0200
committerMark Brown <broonie@kernel.org>2019-07-03 13:21:34 +0100
commit751bd5db52604f3f71d54dbad82707ef2475b707 (patch)
tree914c34f5ae36605e4fae0215c0c1a675e3a6c5ae
parent094380ea2bf9f0fa7d63e67bf500b8c77e8d1910 (diff)
downloadlinux-751bd5db52604f3f71d54dbad82707ef2475b707.tar.gz
ASoC: meson: axg-tdm-formatter: add reset
Add the optional reset line handling which is present on the new SoC
families, such as the g12a. Triggering this reset is not critical but
it helps solve a channel shift issue on the g12a.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20190703120749.32341-3-jbrunet@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index 0c6cce5c5773..2e498201139f 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -7,6 +7,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 #include <sound/soc.h>
 
 #include "axg-tdm-formatter.h"
@@ -20,6 +21,7 @@ struct axg_tdm_formatter {
 	struct clk *lrclk;
 	struct clk *sclk_sel;
 	struct clk *lrclk_sel;
+	struct reset_control *reset;
 	bool enabled;
 	struct regmap *map;
 };
@@ -76,6 +78,24 @@ static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
 		return 0;
 
 	/*
+	 * On the g12a (and possibly other SoCs), when a stream using
+	 * multiple lanes is restarted, it will sometimes not start
+	 * from the first lane, but randomly from another used one.
+	 * The result is an unexpected and random channel shift.
+	 *
+	 * The hypothesis is that an HW counter is not properly reset
+	 * and the formatter simply starts on the lane it stopped
+	 * before. Unfortunately, there does not seems to be a way to
+	 * reset this through the registers of the block.
+	 *
+	 * However, the g12a has indenpendent reset lines for each audio
+	 * devices. Using this reset before each start solves the issue.
+	 */
+	ret = reset_control_reset(formatter->reset);
+	if (ret)
+		return ret;
+
+	/*
 	 * If sclk is inverted, invert it back and provide the inversion
 	 * required by the formatter
 	 */
@@ -306,6 +326,15 @@ int axg_tdm_formatter_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	/* Formatter dedicated reset line */
+	formatter->reset = reset_control_get_optional_exclusive(dev, NULL);
+	if (IS_ERR(formatter->reset)) {
+		ret = PTR_ERR(formatter->reset);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get reset: %d\n", ret);
+		return ret;
+	}
+
 	return devm_snd_soc_register_component(dev, drv->component_drv,
 					       NULL, 0);
 }