diff options
| -rw-r--r-- | sound/soc/meson/axg-tdm-formatter.c | 29 | 
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);  } | 
